在MongoDB中查找不包含特定字段值的文档数组

90

我正在尝试查找所有不包含具有特定字段值的文档的文档。例如,这是一个示例集合:

{  _id : 1,
  docs : [
        { foo : 1,
          bar : 2},
        { foo : 3,
          bar : 3}
         ]
},
{  _id : 2,
  docs : [
        { foo : 2,
          bar : 2},
        { foo : 3,
          bar : 3}
         ]
}

我想查找所有记录,其中文档块中没有包含至少一个foo = 1的记录。在上面的示例中,只有第二个文档应该被返回。

我尝试了以下方法,但它只告诉我是否存在不匹配的记录(这会返回文档1)。

db.collection.find({"docs": { $not: {$elemMatch: {foo: 1 } } } })

更新: 上面的查询实际上是有效的。像很多时候一样,我的数据出了问题,而不是我的代码。

我也查看了$nin操作符,但示例仅显示数组包含原始值列表的情况,而不是附加文档的情况。当我尝试使用以下内容进行操作时,它会查找完整的文档,而不仅仅是我想要的foo字段。

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

有没有使用基本运算符完成这个任务的方法?

3个回答

93

使用$nin是可行的,但是您的语法有误。正确的语法应该是:

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

17
为什么要使用$nin而不是$ne?你不能这样做吗:db.collection.find({'docs.foo': {$ne: 1}})?(即相同的操作,但无需将1放入数组中,例如aedm's answer)。 - tscizzle
6
没问题,如果只有一个值,$ne 也可以使用。 - JohnnyHK
1
$ne 也应该更加高效(更快)。 - ztrange

55
使用$ne运算符:
db.collection.find({'docs.foo': {$ne: 1}})
更新:在这种情况下,我建议不要使用$nin

{'docs.foo': {$ne: 1}}获取所有docs元素,并对于每个元素检查其foo字段是否等于1。如果找到匹配项,则将该文档从结果列表中丢弃。

{'docs.foo': {$nin: [1]}}获取所有docs元素,并检查每个元素的foo字段是否与数组[1]的任何成员匹配。这是一个笛卡尔积,你会将一个数组与另一个数组进行比较,每个元素与每个元素比较。虽然MongoDB可能很聪明并优化此查询,但我假设您仅使用$nin,因为“它与数组有关”。但是,如果您理解在此处执行的操作,您将意识到$nin是多余的,并且可能具有低效性能。


2
这是一个与仅有一个元素的数组进行的笛卡尔积:不确定在这种情况下性能损失是否那么高。我们需要进行基准测试。 - Aymeric Bouzy aybbyk

1
重要的是要注意如何使用$elemMatch可能会导致您得到错误的结果。

根本不是数组文档的一部分:

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

这些都是等价的,尽管按照首选使用顺序排序。

不是数组中唯一的文档:

db.collection.find({'docs.foo': {$elemMatch: {foo: {$ne: 1}}}})

花了我一段时间才意识到这就是我做错的地方。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接