Как фильтровать массив в поддокументе с помощью MongoDB [дубликат]


этот вопрос уже есть ответ здесь:

у меня есть массив в поддокументе, как это

{
    "_id" : ObjectId("512e28984815cbfcb21646a7"),
    "list" : [
        {
            "a" : 1
        },
        {
            "a" : 2
        },
        {
            "a" : 3
        },
        {
            "a" : 4
        },
        {
            "a" : 5
        }
    ]
}

могу ли я фильтровать вложенный документ для A > 3

мой ожидаемый результат ниже

{
    "_id" : ObjectId("512e28984815cbfcb21646a7"),
    "list" : [
        {
            "a" : 4
        },
        {
            "a" : 5
        }
    ]
}

I попробуйте использовать $elemMatch но возвращает первый соответствующий элемент в массиве

мой запрос:

db.test.find( { _id" : ObjectId("512e28984815cbfcb21646a7") }, { 
    list: { 
        $elemMatch: 
            { a: { $gt:3 } 
            } 
    } 
} )

результат возвращает один элемент в массиве

{ "_id" : ObjectId("512e28984815cbfcb21646a7"), "list" : [ { "a" : 4 } ] }

и я пытаюсь использовать агрегат с $match но не работает

db.test.aggregate({$match:{_id:ObjectId("512e28984815cbfcb21646a7"), 'list.a':{$gte:5}  }})

он возвращает все элементы в массиве

{
    "_id" : ObjectId("512e28984815cbfcb21646a7"),
    "list" : [
        {
            "a" : 1
        },
        {
            "a" : 2
        },
        {
            "a" : 3
        },
        {
            "a" : 4
        },
        {
            "a" : 5
        }
    ]
}

могу ли я фильтровать элемент в массиве, чтобы получить результат как ожидаемый результат?

3 57

3 ответа:

используя aggregate - это правильный подход, но нужно $unwind the list массив перед нанесением $match так что вы можете фильтровать отдельные элементы, а затем использовать $group чтобы собрать его обратно:

db.test.aggregate(
    { $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
    { $unwind: '$list'},
    { $match: {'list.a': {$gt: 3}}},
    { $group: {_id: '$_id', list: {$push: '$list.a'}}})

выходы:

{
  "result": [
    {
      "_id": ObjectId("512e28984815cbfcb21646a7"),
      "list": [
        4,
        5
      ]
    }
  ],
  "ok": 1
}

Обновление MongoDB 3.2

начиная с версии 3.2, вы можете использовать новый тег $filter оператор агрегации, чтобы сделать это более эффективно, только включая list элементы, которые вы хотите во время $project:

db.test.aggregate([
    { $match: {_id: ObjectId("512e28984815cbfcb21646a7")}},
    { $project: {
        list: {$filter: {
            input: '$list',
            as: 'item',
            cond: {$gt: ['$$item.a', 3]}
        }}
    }}
])

выше решение работает лучше, если требуется несколько вложенных документов. $elemMatch также поставляется в очень использовать, если один соответствующий sub документ требуется в качестве вывода

db.test.find({list: {$elemMatch: {a: 1}}}, {'list.$': 1})

результат:

{
  "_id": ObjectId("..."),
  "list": [{a: 1}]
}

использовать фильтр$агрегации

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

db.test.aggregate([
    {$match: {"list.a": {$gt:3}}}, // <-- match only the document which have a matching element
    {$project: {
        list: {$filter: {
            input: "$list",
            as: "list",
            cond: {$gt: ["$$list.a", 3]} //<-- filter sub-array based on condition
        }}
    }}
]);