Another interesing way is to use $redact, which is one of the new aggregation features of MongoDB 2.6. If you are using 2.6, you don't need an $unwind which might cause you performance problems if you have large arrays.
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact
"restricts the contents of the documents based on information stored in the documents themselves". So it will run only inside of the document. It basically scans your document top to the bottom, and checks if it matches with your if
condition which is in $cond
, if there is match it will either keep the content($$DESCEND
) or remove($$PRUNE
).
In the example above, first $match
returns the whole shapes
array, and $redact strips it down to the expected result.
Note that {$not:"$color"}
is necessary, because it will scan the top document as well, and if $redact
does not find a color
field on the top level this will return false
that might strip the whole document which we don't want.