How to filter an array in a subtask using MongoDB

I have an array in a sub, similar to this

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

Can I filter the subdocument for> 3

My expected result below

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

I try to use $elemMatch but returns the first matching element in the array

My request:

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

The result returns one element in the array.

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

and I'm trying to use an aggregate with $match but not work

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

It returns the whole element in the array

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

Can I filter an element in an array to get the result as the result of the result?

+74
filter mongodb mongodb-query aggregation-framework
Feb 27 '13 at 16:21
source share
3 answers

Using aggregate is the right approach, but you need a $unwind list array before applying $match so you can filter out individual elements and then use $group to put it together:

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

outputs:

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

MongoDB 3.2 Update

Starting with version 3.2, you can use the new $filter aggregation operator to do this more efficiently by including only the list elements that you want during $project :

 db.test.aggregate([ { $match: {_id: ObjectId("512e28984815cbfcb21646a7")}}, { $project: { list: {$filter: { input: '$list', as: 'item', cond: {$gt: ['$$item.a', 3]} }} }} ]) 
+111
Feb 27 '13 at 17:04 on
source share

The above solution works best if you need several suitable subdomains. $ elemMatch is also very useful if a separate document document is required as output

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

Result:

 { "_id": ObjectId("..."), "list": [{a: 1}] } 
+17
Dec 03 '15 at 11:07
source share

Use $ filter aggregation

Selects a subset of the array to return based on the specified condition. Returns an array with only those elements that match the condition. The returned items are in the original order.

 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 }} }} ]); 
+10
Mar 18 '17 at 15:48
source share



All Articles