将布尔字段查询为“不正确”(例如,错误或不存在)

Posted

技术标签:

【中文标题】将布尔字段查询为“不正确”(例如,错误或不存在)【英文标题】:Query for boolean field as "not true" (e.g. either false or non existent) 【发布时间】:2013-09-21 03:53:06 【问题描述】:

我确定我在 MongoDB 查询中遗漏了一些非常基本的东西,似乎无法得到这个简单的条件。

考虑这个系列

> db.tests.find()
 "_id" : ObjectId("..."), "name" : "Test1" , "deleted" : true
 "_id" : ObjectId("..."), "name" : "Test2" , "deleted" : false
 "_id" : ObjectId("..."), "name" : "Test3" 

我只想查询所有“未删除”的项目

我知道如何找到将“已删除”标志设置为 true 的项目:

> db.tests.find(deleted:true)
 "_id" : ObjectId("..."), "name" : "Test1" , "deleted" : true

但是我如何找到所有不是"deleted" 的项目(例如否定上述查询,或者换句话说,任何没有"deleted" 字段或具有值false 的项目

我猜测的结果(请不要笑...)

> db.tests.find($not : deleted: true)

(不返回任何结果)

> db.tests.find($not : $eq:deleted:true)

error: "$err" : "无效操作符: $eq", "code" : 10068

> db.tests.find(deleted:$not: true)

error: "$err" : "$not 的使用无效", "code" : 13041

> db.tests.find(deleted:$not: $eq:true)

error: "$err" : "$not 的使用无效", "code" : 13034

我错过了什么?

【问题讨论】:

【参考方案1】:

如果您正在寻找 mongoid 语法(我在 Rails 应用程序中使用它),这是我为公司用户想出的:

2.3.1 :042 > accepted_consent = org.users.active.where(:accepted_terms_and_conditions => true).count
 => 553 
2.3.1 :043 > not_accepted_yet = org.users.active.where(:accepted_terms_and_conditions.ne => true).count
 => 6331 
2.3.1 :044 > 6331+553
 => 6884 
2.3.1 :045 > org.users.active.count
 => 6884 

【讨论】:

【参考方案2】:

如果有人在聚合管道中需要它而不是 find,这对我有用

db.getCollection('tests').aggregate([ 
  // ...previous operations...
   $addFields:  "deleted_conclusion":  $cond: 
        if: $ne: [ "$deleted", false ], then:  $cond: [ "$deleted", ":TRUE", ":FALSY"], else: ":FALSE"
  
])

添加额外字段后,您可以继续流水线阶段并获取您错过的信息

【讨论】:

【参考方案3】:

JohnnyHK 给出了最好的答案。 $in 选择器是最短和最干净的 IMO。

这将完全测试“假”或“不存在”。并且可以被索引。

db.tests.find($or:[deleted:false,deleted:$exists:false])

一个使用索引的例子。

((function()
    print("creating collection 'testx' and inserting 50 trues, 50 falses, 50 non-existents");
    db.testx.drop();
    db.testx.ensureIndex(deleted:1);
    for (var i=0;i<50;i++)
        db.testx.insert(i:i,deleted:false);
    ;
    for (var i=0;i<50;i++)
        db.testx.insert(i:i,deleted:true);
    ;
    for (var i=0;i<50;i++)
        db.testx.insert(i:i);
    ;
    var res0 = db.testx.find().explain();
    var res1 = db.testx.find(deleted:false).explain();
    var res2 = db.testx.find(deleted:true).explain();
    var res3 = db.testx.find(deleted:$exists:false).explain();
    var res4 = db.testx.find($or:[deleted:false,deleted:$exists:false]).explain();
    var res5 = db.testx.find($or:[deleted:true,deleted:$exists:false]).explain();
    var res6 = db.testx.find(deleted:$in:[false,null]).explain();
    print("res0: all objects                      ("+res0["n"]+" found, "+res0["nscannedObjects"]+" scanned)");
    print("res1: deleted is false                 ("+res1["n"]+" found, "+res1["nscannedObjects"]+" scanned)");
    print("res2: deleted is true                  ("+res2["n"]+" found, "+res2["nscannedObjects"]+" scanned)");
    print("res3: deleted is non-existent          ("+res3["n"]+" found, "+res3["nscannedObjects"]+" scanned)");
    print("res4: deleted is false or non-existent ("+res4["n"]+" found, "+res4["nscannedObjects"]+" scanned)");
    print("res5: deleted is true or non-existent  ("+res5["n"]+" found, "+res5["nscannedObjects"]+" scanned)");
    print("res6: deleted is in [false,null]       ("+res5["n"]+" found, "+res5["nscannedObjects"]+" scanned)");
)())

这应该打印出来

creating collection 'testx' and inserting 50 trues, 50 falses, 50 non-existents
res0: all objects                      (150 found, 150 scanned)
res1: deleted is false                 (50 found, 50 scanned)
res2: deleted is true                  (50 found, 50 scanned)
res3: deleted is non-existent          (50 found, 50 scanned)
res4: deleted is false or non-existent (100 found, 100 scanned)
res5: deleted is true or non-existent  (100 found, 100 scanned)
res6: deleted is in [false,null]       (100 found, 100 scanned)

【讨论】:

当我对此查询进行解释时,它显示索引仅用于查询的“已删除:假”部分。 'deleted:$exists:false' 似乎没有使用索引。 @emilebaizel $exists 应该可用于索引。 Mongo jira.mongodb.org/browse/SERVER-10608 啊哈!大概就是这样。我们在 2.4。【参考方案4】:

为了完整起见,另一种方法是使用$in

db.test.find(deleted: $in: [null, false])

在数组中包含null 会拉入缺少deleted 字段的文档。此查询可以在当前 2.6.6 MongoDB 版本中使用deleted: 1 上的索引。

【讨论】:

【参考方案5】:
db.tests.find(deleted: $ne: true)

$ne 代表“不等于”。 (Documentation on mongodb operators)

【讨论】:

根据文档,使用 $ne 似乎没有利用索引 - 是否有另一种能够利用索引的解决方案? docs.mongodb.org/manual/faq/indexes/… 只是为其他读者重新迭代 - 此查询不会使用索引。所以请不要在生产中使用。 那么在生产中应该使用什么来代替? @Juhana 我刚刚用 MongoDB 2.6.6 测试了它,它确实现在使用为 deleted: 1 创建的索引。 在 3.6.8 下使用复合索引未正确索引,但 $in: [false,null] 似乎有效。

以上是关于将布尔字段查询为“不正确”(例如,错误或不存在)的主要内容,如果未能解决你的问题,请参考以下文章

WCF 给出不安全或不正确安全的故障错误

JQ:如果字段为空或不存在,如何删除?

Django 表单中的 CSRF 令牌丢失或不正确

AWS 环境中的 WCF UserNameTransport 错误 - 收到不安全或不正确安全的故障

Django 会话导致 Angular 出现 JWT“令牌丢失或不正确”错误

为啥我会收到“CSRF 令牌丢失或不正确”错误?