Reputation: 21260
I want to find only documents that have all 'docs.foo'
present in a array.
In my example I should get only _id: 2
result:
{
_id : 1,
docs : [
{ bar : 2},
{ foo : 3, bar : 3}
]
},
{
_id : 2,
docs : [
{ foo : 2, bar : 2},
{ foo : 3, bar : 3}
]
}
I thought about something like:
db.collection.find({'docs.foo': {$nin: [$exists: false]}})
but couldn't make it work.
Upvotes: 2
Views: 134
Reputation: 61225
Using the $where
operator.
db.collection.find(function() {
return this.docs.length === this.docs.filter(function(doc) {
return typeof(doc.foo) !== "undefined" && doc.foo !== null ;}).length
})
Another way to do this is to run two queries: One to retrieve the _id
of all those documents that don't match your criteria using the distinct()
method:
var unwantedIds = db.collection.distinct( "_id", { "docs": { "$elemMatch": { "foo": { "$exists": false } } } } );
Then use the $nin
operator to return all those documents that match your criteria.
db.collection.find({ "_id": { "$nin": unwantedIds } } )
You can also use the .aggregate()
method but this only work if you are on version 3.2 or newer because you need to use the $filter
First stage in the pipeline is the $match
stage where you filter out those documents where the "foo" field is absent. This reduce the total number of documents that will be processed down the pipe. The next and last stage is the $redact
stage. In this stage you need to use the $size
operator to return the size of the the "docs" field and the size of the array of the sub-documents in where "foo" is present and return all those documents where the two values are equals.
db.collection.aggregate([
{ "$match": { "docs.foo": { "$exists": true } } },
{ "$redact": {
"$cond": [
{ "$eq": [
{ "$size": "$docs" },
{ "$size": {
"$filter": {
"input": "$docs",
"as": "doc",
"cond": {
"$ne": [
{ "$ifNull": [ "$$doc.foo", null ] },
null
]
}
}
}}
]},
"$$KEEP",
"$$PRUNE"
]
}}
])
Upvotes: 3