Mongoose - finding subdocuments by criteria
In Mongoose, you can also use the elegant .populate()
function like this:
parents
.find({})
.populate({
path: 'children',
match: { age: { $gte: 18 }},
select: 'name age -_id'
})
.exec()
You can use $elemMatch
as a query-projection operator in the most recent MongoDB versions. From the mongo shell:
db.parents.find(
{'children.age': {$gte: 18}},
{children:{$elemMatch:{age: {$gte: 18}}}})
This filters younger children's documents out of the children
array:
{ "_id" : ..., "children" : [ { "name" : "Margaret", "age" : 20 } ] }
{ "_id" : ..., "children" : [ { "name" : "John", "age" : 22 } ] }
As you can see, children are still grouped inside their parent documents. MongoDB queries return documents from collections. You can use the aggregation framework's $unwind
method to split them into separate documents:
> db.parents.aggregate({
$match: {'children.age': {$gte: 18}}
}, {
$unwind: '$children'
}, {
$match: {'children.age': {$gte: 18}}
}, {
$project: {
name: '$children.name',
age:'$children.age'
}
})
{
"result" : [
{
"_id" : ObjectId("51a7bf04dacca8ba98434eb5"),
"name" : "Margaret",
"age" : 20
},
{
"_id" : ObjectId("51a7bf04dacca8ba98434eb6"),
"name" : "John",
"age" : 22
}
],
"ok" : 1
}
I repeat the $match
clause for performance: the first time through it eliminates parents with no children at least 18 years old, so the $unwind
only considers useful documents. The second $match
removes $unwind
output that doesn't match, and the $project
hoists children's info from subdocuments to the top level.