ChatGPT解决这个技术问题 Extra ChatGPT

Find documents with arrays not containing a document with a particular field value in 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?


J
JohnnyHK

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

db.collection.find({'docs.foo': {$nin: [1]}})

Curious, why $nin instead of $ne? Couldn't you do db.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)
@tscizzle Yep, $ne is fine as well if there's only one value.
$ne also should be a little more efficient (quicker)
a
aedm

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.


It's a cartesian product with an array with only one element : not sure performance hit is that high in that case. We would have to make a benchmark.