I'm trying to find all documents that do not contain at least one document with a specific field value. For example here is a sample collection:
{ _id : 1,
docs : [
{ foo : 1,
bar : 2},
{ foo : 3,
bar : 3}
]
},
{ _id : 2,
docs : [
{ foo : 2,
bar : 2},
{ foo : 3,
bar : 3}
]
}
I want to find every record where there is not a document in the docs block that does not contain at least one record with foo = 1. In the example above, only the second document should be returned.
I have tried the following, but it only tells me if there are any that don't match (which returns document 1.
db.collection.find({"docs": { $not: {$elemMatch: {foo: 1 } } } })
UPDATE: The query above actually does work. As many times happens, my data was wrong, not my code.
I have also looked at the $nin operator but the examples only show when the array contains a list of primitive values, not an additional document. When I've tried to do this with something like the following, it looks for the EXACT document rather than just the foo field I want.
db.collection.find({"docs": { $nin: {'foo':1 } } })
Is there anyway to accomplish this with the basic operators?
Using $nin
will work, but you have the syntax wrong. It should be:
db.collection.find({'docs.foo': {$nin: [1]}})
Use the $ne
operator:
db.collection.find({'docs.foo': {$ne: 1}})
Update: I'd advise against using $nin
in this case.
{'docs.foo': {$ne: 1}}
takes all elements of docs
, and for each of them it checks whether the foo
field equals 1 or not. If it finds a match, it discards the document from the result list.
{'docs.foo': {$nin: [1]}}
takes all elements of docs
, and for each element it checks whether its foo
field matches any of the members of the array [1]
. This is a Cartesian product, you compare an array to another array, each element to each element. Although MongoDB might be smart and optimize this query, I assume you only use $nin
because "it has do to something with arrays". But if you understand what you do here, you'll realize $nin
is superfluous, and has possibly subpar performance.
Success story sharing
$nin
instead of$ne
? Couldn't you dodb.collection.find({'docs.foo': {$ne: 1}})
? (i.e. the same thing but no need for 1 to be in an array, as in aedm's answer)$ne
is fine as well if there's only one value.$ne
also should be a little more efficient (quicker)