Запит для логічного поля як "не відповідає дійсності" (наприклад, помилковий або неіснуючий)


77

Я впевнений, що мені не вистачає чогось дуже елементарного в запитах 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}

Чого мені не вистачає?

Відповіді:


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

Де $neозначає "не рівний". ( Документація щодо операторів mongodb )


2
Згідно з документами, використання $ ne, здається, не використовує індекс - чи існує інше рішення, яке могло б скористатися перевагами індексу? docs.mongodb.org/manual/faq/indexes/…
joelsand

2
Просто повторити ітерацію для інших читачів - цей запит не використовуватиме індекс . Тому, будь ласка, не використовуйте у виробництві.
UpTheCreek

То що замість цього слід використовувати у виробництві?
JJJ

9
@Juhana я тільки перевірив це з MongoDB 2.6.6 і робить тепер використовувати індекс , створений для {deleted: 1}.
JohnnyHK

2
Не правильно проіндексував під 3.6.8 із складеними індексами, але, $in: [false,null]здавалося, спрацював.
jimrandomh

31

Для повноти інший спосіб зробити це за допомогою $in:

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

Включаючи nullв масив витягує документи, де deletedполе відсутнє. Цей запит може використовувати індекс {deleted: 1}у поточному випуску 2.6.6 MongoDB.


6

JohnnyHK має найкращу відповідь. $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)

Коли я роблю пояснення щодо цього запиту, він показує, що індекс використовується лише для частини запиту „delete: false”. Здається, "видалено: {$ існує: помилково}" не використовує індекс.
emilebaizel

@emilebaizel $ існує має бути доступним для індексації. Старі версії Mongo <2.5.5 можуть не мати цієї функції. jira.mongodb.org/browse/SERVER-10608
tidwall

ага! це, мабуть, усе. ми на 2.4.
emilebaizel

1

У випадку, коли комусь це потрібно замість цього в конвеєрі агрегування find, це те, що мені вдалося

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

Після додавання додаткового поля ви зможете продовжувати етапи конвеєра та мати інформацію, яку ви пропустите


0

Якщо ви шукаєте монгоїдний синтаксис (я використовую це в додатку 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 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.