MongoDB elemmatch несколько элементов в массиве

У меня есть документ Mongodb, как

    {
        "_id" : ObjectId("54e66b2da7b5f3a92e09dc6c"),
        "SomeMetric" : [ 
            {
                //some object
            }
            {
                //some object
            } 
         ],
        "FilterMetric" : [ 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s1"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s2"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s1"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s2"
            } 
        ]
    }

Обычно он содержит много таких вложенных массивов. Я хочу спроектировать одну метрику в одиночку, только с массивами, которые имеют мои критерии поиска. У меня есть запрос

db.sample.find(
{"filtermetric.class" : "s2"},{"filtermetric" : { $elemMatch : {class: "s2"}}}
)

Это дает мне только первый объект в массиве. Второй объект с классом: s2 не возвращается.

Если я попробую

    db.sample.find(
   {"filtermetric" : { $elemMatch : {class: "s2"}}}
    )

Это дает мне все 4 объекта в массиве.

Как мне получить все объекты, которые соответствуют критериям в таком случае?

1 ответ

Решение

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

Основное отличие здесь заключается в том, что "запрос" делает именно то, для чего он предназначен, и соответствует "документам", которые соответствуют вашим условиям. Вы можете попробовать использовать позиционный $ оператор в аргументе проекции, но правила таковы, что он будет соответствовать только "первому" элементу массива, который соответствует условиям запроса.

Чтобы "отфильтровать" несколько элементов массива, выполните следующие действия:

db.sample.aggregate([
    // Filter possible documents
    { "$match": { "filtermetric.class": "s2" } },

    // Unwind the array to denormalize
    { "$unwind": "$filtermetric" },

    // Match specific array elements
    { "$match": { "filtermetric.class": "s2" } },

    // Group back to array form
    { "$group": {
        "_id": "$_id",
        "filtermetric": { "$push": "$filtermetric" }
    }}
])

В современных версиях MongoDB версии 2.6 или выше вы можете сделать это с $redact:

db.sample.aggregate([
    // Filter possible documents
    { "$match": { "filtermetric.class": "s2" } },

    // Redact the entries that do not match
    { "$redact": {
        "$cond": [
            { "$eq": [ { "$ifNull": [ "$class", "s2" ] }, "s2" ] },
            "$$DESCEND",
            "$$PRUNE"
        ]
    }}
])

Вероятно, это наиболее эффективный вариант, но он рекурсивный, поэтому сначала рассмотрите структуру документа, поскольку одноименное поле не может существовать с какими-либо другими условиями на любом уровне.

Возможно, безопаснее, но только в том случае, если результаты в массиве "действительно уникальны", если этот метод $map а также $setDifference:

db.sample.aggregate([
    { "$project": {
        "filtermetric": { "$setDifference": [
            { "$map": [
                "input": "$filtermetric",
                "as": "el",
                "in": {"$cond": [
                    { "$eq": [ "$$el.class", "s2" ] },
                    "$$el",
                    false
                ]}
            ]},
            [false]
        ]}
    }}
])

Также отмечая, что в обоих $group а также $project Этапы рабочего конвейера: вам нужно указать все поля, которые вы намереваетесь вернуть в ваших документах с результатами этого этапа.

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

Другие вопросы по тегам