Регулярное выражение внутри массива в mongoDB

я хочу сделать запрос внутри массива в mongodb с регулярным выражением, в коллекциях есть такие документы:

{
"_id" : ObjectId("53340d07d6429d27e1284c77"),
"company" : "New Company",
"worktypes" : [ 
    {
        "name" : "Pompas",
        "works" : [ 
            {
                "name" : "name 2",
                "code" : "A00011",
                "price" : "22,22"
            }, 
            {
                "name" : "name 3",
                "code" : "A00011",
                "price" : "22,22"
            }, 
            {
                "name" : "name 4",
                "code" : "A00011",
                "price" : "22,22"
            }, 
            {
                "code" : "asdasd",
                "name" : "asdads",
                "price" : "22"
            }, 
            {
                "code" : "yy",
                "name" : "yy",
                "price" : "11"
            }
        ]
    }, 
    {
        "name" : "name 4",
        "works" : [ 
            {
                "code" : "A112",
                "name" : "Nombre",
                "price" : "11,2"
            }
        ]
    },          
    {
        "name" : "ee",
        works":[

            {
                "code" : "aa",
                "name" : "aa",
                "price" : "11"
            }, 
            {
                "code" : "A00112",
                "name" : "Nombre",
                "price" : "12,22"
            }
              ]
    }
]

}

Затем мне нужно найти документ по названию компании, и любая работа внутри него соответствует регулярному выражению в коде или имени. У меня есть это:

var companyquery = { "company": "New Company"};
var regQuery = new RegExp('^A0011.*$', 'i');

db.categories.find({$and: [companyquery,
            {$or: [
                {"worktypes.works.$.name": regQuery},
                {"worktypes.works.$.code": regQuery}
            ]}]})

Но не возвращайте никакого результата. Я думаю, что ошибка заключается в попытке поиска внутри массива с точкой и $ .. Любая идея?

Редактировать:

С этим:

db.categories.find({$and: [{"company":"New Company"},
            {$or: [
                {"worktypes.works.name": {"$regex": "^A00011$|^a00011$"}},
                {"worktypes.works.code": {"$regex": "^A00011$|^a00011$"}}
            ]}]})

Вот результат:

{
    "_id" : ObjectId("53340d07d6429d27e1284c77"),
    "company" : "New Company",
    "worktypes" : [ 
        {
            "name" : "Pompas",
            "works" : [ 
                {
                    "name" : "name 2",
                    "code" : "A00011",
                    "price" : "22,22"
                }, 
                {
                    "code" : "aa",
                    "name" : "aa",
                    "price" : "11"
                }, 
                {
                    "code" : "A00112",
                    "name" : "Nombre",
                    "price" : "12,22"
                }, 
                {
                    "code" : "asdasd",
                    "name" : "asdads",
                    "price" : "22"
                }, 
                {
                    "code" : "yy",
                    "name" : "yy",
                    "price" : "11"
                }
            ]
        }, 
        {
            "name" : "name 4",
            "works" : [ 
                {
                    "code" : "A112",
                    "name" : "Nombre",
                    "price" : "11,2"
                }
            ]
        }, 
        {
            "name" : "Bombillos"
        }, 
        {
            "name" : "Pompas"
        }, 
        {
            "name" : "Bombillos 2"
        }, 
        {
            "name" : "Other type"
        }, 
        {
            "name" : "Other new type"
        }
    ]
}

Регулярное выражение не выводит результаты в порядке.


person colymore    schedule 07.04.2014    source источник
comment
Что такое regexQuery ?   -  person Robin    schedule 07.04.2014
comment
Это строка со значением для регулярного выражения, я скопировал свой код из клиента nodejs, я отредактирую свой вопрос. спасибо   -  person colymore    schedule 07.04.2014
comment
Я думаю, что одна из проблем заключается в том, что позиционный оператор $ является оператором проекции/обновления, а не оператором запроса, попробуйте что-нибудь ближе к: "worktypes.works.name", а не "worktypes.works.$.name"   -  person Sammaye    schedule 07.04.2014
comment
Тот же результат.. нечего показать :(   -  person colymore    schedule 07.04.2014
comment
Подождите, какой результат вы хотите? Этот документ заполняет результаты   -  person Sammaye    schedule 07.04.2014
comment
Я хочу отбросить работы, которые не являются математическим выражением регулярного выражения, чтобы получить работы только с именем или кодом с A001.   -  person colymore    schedule 07.04.2014
comment
Вам нужно будет использовать структуру агрегации, дважды раскручивая, а затем перегруппировывая   -  person Sammaye    schedule 07.04.2014


Ответы (2)


Вы используете нативный объект JavaScript RegExp для регулярного выражения, однако для того, чтобы mongo обработал регулярное выражение, его необходимо отправить как часть документа запроса, а это не одно и то же.

Также регулярное выражение не будет соответствовать нужным вам значениям. На самом деле это может быть ^A0111$ для точного совпадения, но ваше совпадение без учета регистра вызывает проблему, вызывающую большее сканирование возможного индекса. Так что есть лучший способ написать это. Также см. ссылку на документацию по проблемам с нечувствительными к регистру совпадениями.

Вместо этого используйте оператор $regex:

db.categories.find({
    "$and": [
        {"company":"New Company"},
        { "$or": [
            { "worktypes.works.name": { "$regex": "^A00011$|^a00011$" }},
            { "worktypes.works.code": { "$regex": "^A00011$|^a00011$" }}
        ]}
    ]
})

Также позиционные заполнители $ недействительны для запроса, они используются только в проекции или обновлении или первом соответствующем элементе, найденном запросом.

Но ваша реальная проблема, похоже, заключается в том, что вы пытаетесь получить только те элементы массива, которые «соответствуют» вашим условиям. Вы не можете сделать это с .find(), и для этого вам нужно использовать .aggregate() вместо этого:

db.categories.aggregate([

    // Always makes sense to match the actual documents
    { "$match": {
        "$and": [
            {"company":"New Company"},
            { "$or": [
                { "worktypes.works.name": { "$regex": "^A00011$|^a00011$" }},
                { "worktypes.works.code": { "$regex": "^A00011$|^a00011$" }}
            ]}
        ]
    }},

    // Unwind the worktypes array
    { "$unwind": "$worktypes" },

    // Unwind the works array
    { "$unwind": "$worktypes.works" },

    // Then use match to filter only the matching entries
    { "$match": {
       "$or": [
            { "worktypes.works.name": { "$regex": "^A00011$|^a00011$" } },
            { "worktypes.works.code": { "$regex": "^A00011$|^a00011$" } }
        ]
    }},

    /* Stop */
    // If you "really" need the arrays back then include all the following
    // Otherwise the steps up to here actually got you your results

    // First put the "works" array back together
    { "$group": {
        "_id": {
            "_id": "$_id",
            "company": "$company",
            "workname": "$worktypes.name"
        },
        "works": { "$push": "$worktypes.works" }
    }},

    // Then put the "worktypes" array back
    { "$group": {
        "_id": "$_id._id",
        "company": { "$first": "$_id.company" },
        "worktypes": {
            "$push": {
                "name": "$_id.workname",
                "works": "$works"
            } 
        } 
    }}
])

Так что же делает .aggregate() с все эти этапы разбивают элементы массива на обычную форму документа, чтобы их можно было отфильтровать с помощью оператора $match. Таким образом, возвращаются только те элементы, которые «соответствуют».

Что «найти» правильно делает, так это сопоставляет «документ», который соответствует условиям. Поскольку документы содержат совпадающие элементы, они возвращаются. Эти два принципа — очень разные вещи.

Когда вы имеете в виду «фильтровать», используйте агрегат.

person Neil Lunn    schedule 07.04.2014
comment
@colymore Ну, документ в результате действительно содержит элементы, соответствующие выражению. Так вы на самом деле пытаетесь отфильтровать результаты массива, чтобы возвращать только совпадения? Это другое дело. - person Neil Lunn; 07.04.2014
comment
Я пытаюсь получить только те рабочие типы. - person colymore; 07.04.2014
comment
Объект RegExp будет преобразован в объект регулярного выражения BSON, как показано в документации. Объект регулярного выражения BSON не преобразуется напрямую в оператор $regex. - person Sammaye; 07.04.2014
comment
@colymore Именно это я и сказал в своем комментарии. Вы на самом деле просите что-то, что find не делает. Смотрите дополнительную информацию. - person Neil Lunn; 07.04.2014
comment
@Sammaye, кажется, вы имеете в виду использование регулярных выражений в оболочке монго. Это не то же самое, что работать с родным драйвером для узла или большинства других языков. Преобразование в отказ от использования типа объекта RegExp JavaScript фактически сработало из редактирования, опубликованного OP. Единственная проблема заключается в интерпретации, что ожидаемый результат должен возвращать только совпадающие элементы массива. - person Neil Lunn; 07.04.2014
comment
@NeilLunn, я пытаюсь использовать ваш код, но не возвращаю никакого результата внутри массива результатов.. Мне нужно отредактировать некоторые (чтобы правильно скомпилировать, но не возвращать результаты.. - person colymore; 07.04.2014
comment
@colymore ты действительно остановился, когда комментарии сказали тебе остановиться? Остальные этапы не должны требоваться для того, что вы, кажется, хотите. В основном я хочу, чтобы вы увидели разницу между совпадающими документами и совпадающими элементами массива. - person Neil Lunn; 07.04.2014
comment
@colymore Итак, я действительно потратил время, воспроизвел ваши данные и прогнал полученное окончательное совокупное выражение. Правки есть в ответе, и вся цепочка работает как положено. Поэтому у вас нет причин не принимать это. Я указал на несколько проблем с вашим первоначальным подходом и дал правильное решение для фактической фильтрации массива. Было бы прилично на самом деле принять совет, который вам дали :) - person Neil Lunn; 07.04.2014
comment
Спасибо за ваше время, и ваше предложение ..!! - person colymore; 07.04.2014
comment
@colymore Также может помочь, если вы действительно примете ответы на другие заданные вами вопросы. Или хотя бы приняли правильные :) И если вы еще и проголосовали за полезную информацию. - person Neil Lunn; 07.04.2014

я думаю там опечатка:

регулярное выражение должно быть: ^A00011.*$

тройной 0 вместо двойного 0

person aelor    schedule 07.04.2014
comment
Не, тот же результат. Я пробовал с большинством кодов и имен, но ни один из них не работает. - person colymore; 07.04.2014