Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find documents with arrays not containing a document with a particular field value in MongoDB

Tags:

mongodb

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?

like image 742
Scott Avatar asked Apr 25 '13 18:04

Scott


People also ask

How do I search an array in MongoDB?

To search the array of object in MongoDB, you can use $elemMatch operator. This operator allows us to search for more than one component from an array object.

How do I search multiple documents in MongoDB?

You can query for multiple documents in a collection with collection. find() . The find() method uses a query document that you provide to match the subset of the documents in the collection that match the query.

What does find () do in MongoDB?

Find() Method. In MongoDB, find() method is used to select documents in a collection and return a cursor to the selected documents.


2 Answers

Using $nin will work, but you have the syntax wrong. It should be:

db.collection.find({'docs.foo': {$nin: [1]}}) 
like image 136
JohnnyHK Avatar answered Oct 11 '22 13:10

JohnnyHK


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.

like image 24
aedm Avatar answered Oct 11 '22 13:10

aedm