Запрос на логическое поле как "неправда" (например, либо ложные или несуществующие)


Я уверен, что мне не хватает чего-то очень простого в запросах 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}}})

ошибка: {"$err": "недопустимый оператор: $eq", "код": 10068 }

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

ошибка: {"$err": "недопустимое использование $not", "код": 13041 }

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

ошибка: {"$err": "недопустимое использование $not", "код": 13034 }

что я не хватает?

5   51   2013-09-17 01:18:20

5 ответов:

db.tests.find({deleted: {$ne: true}})

здесь $ne означает "не равно". (документация по операторам mongodb)

для полноты картины, другой способ сделать это-с $in:

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

в том числе null в массиве тянет в документах, где deleted поле отсутствует. Этот запрос может использовать индекс на {deleted: 1} в текущем выпуске 2.6.6 MongoDB.

Джонник имеет лучший ответ. Элемент $in селектор является самым коротким и чистым ИМО.

это будет проверка на точно "ложные"или " несуществующие". И может быть проиндексирован.

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)

в случае, если кому-то это нужно в конвейере агрегации вместо find, это то, что сработало для меня

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

после добавления дополнительного поля вы можете перейти к этапам конвейера и получить информацию, которую вы пропустите

в случае, если вы ищете синтаксис 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