92 votes

Interroger le champ booléen comme "pas vrai" (par exemple, soit faux soit inexistant)

Je suis sûr de manquer quelque chose de très basique dans les requêtes MongoDB, je n'arrive pas à obtenir cette condition simple.

Considérez cette collection

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

J'aimerais simplement interroger tous les éléments qui ne sont pas "supprimés"

Je sais comment trouver l'élément qui a un indicateur "supprimé" défini sur true:

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

Mais comment trouver tous les éléments qui NE sont PAS "supprimés" (par exemple, nier la requête ci-dessus, ou en d'autres termes, tous les éléments qui n'ont pas un champ "supprimé", ou l'ont avec la valeur false)

Ce que j'ai essayé en devinant (ne riez pas...)

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

(ne retourne aucun résultat)

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

erreur: { "$err" : "opérateur non valide: $eq", "code" : 10068 }

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

erreur: { "$err" : "utilisation non valide de $not", "code" : 13041 }

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

erreur: { "$err" : "utilisation non valide de $not", "code" : 13034 }

Qu'est-ce que j'oublie?

172voto

Sergio Tulentsev Points 82783
db.tests.find({deleted: {$ne: true}})

$ne signifie "non égal". (Documentation sur les opérateurs mongodb)

37voto

JohnnyHK Points 61191

Pour des raisons de complétude, une autre façon de le faire est avec $in :

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

Inclure null dans le tableau fait apparaître les documents où le champ deleted est manquant. Cette requête peut utiliser un index sur {deleted: 1} dans la version actuelle de MongoDB 2.6.6.

7voto

jojaba Points 3915

JohnnyHK a la meilleure réponse. Le sélecteur $in est le plus court et le plus propre à mon avis.

Cela testera exactement "faux" ou "non existant". Et peut être indexé.

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

Un exemple avec l'utilisation d'un index.

((function(){
    print("création de la collection 'testx' et insertion de 50 vrais, 50 faux, 50 non-existants");
    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: tous les objets                      ("+res0["n"]+" trouvés, "+res0["nscannedObjects"]+" scannés)");
    print("res1: supprimé est faux                 ("+res1["n"]+" trouvé, "+res1["nscannedObjects"]+" scannés)");
    print("res2: supprimé est vrai                  ("+res2["n"]+" trouvé, "+res2["nscannedObjects"]+" scannés)");
    print("res3: supprimé est non-existent          ("+res3["n"]+" trouvé, "+res3["nscannedObjects"]+" scannés)");
    print("res4: supprimé est faux ou non-existent ("+res4["n"]+" trouvé, "+res4["nscannedObjects"]+" scannés)");
    print("res5: supprimé est vrai ou non-existent  ("+res5["n"]+" trouvé, "+res5["nscannedObjects"]+" scannés)");
    print("res6: supprimé est dans [faux,null]       ("+res5["n"]+" trouvé, "+res5["nscannedObjects"]+" scannés)");
})())

Cela devrait afficher

création de la collection 'testx' et insertion de 50 vrais, 50 faux, 50 non-existants
res0: tous les objets                      (150 trouvés, 150 scannés)
res1: supprimé est faux                 (50 trouvé, 50 scannés)
res2: supprimé est vrai                  (50 trouvé, 50 scannés)
res3: supprimé est non-existant          (50 trouvé, 50 scannés)
res4: supprimé est faux ou non-existant (100 trouvé, 100 scannés)
res5: supprimé est vrai ou non-existant  (100 trouvé, 100 scannés)
res6: supprimé est dans [faux,null]       (100 trouvé, 100 scannés)

1voto

JohnPan Points 574

Pour le cas où quelqu'un aurait besoin de cela dans un pipeline d'agrégation au lieu de find, voici ce qui a fonctionné pour moi

db.getCollection('tests').aggregate([ 
  // ...opérations précédentes...
  { $addFields: { "deleted_conclusion": { $cond: {
        if:{ $ne: [ "$deleted", false ]}, then: { $cond: [ "$deleted", ":TRUE", ":FALSY"]}, else: ":FALSE"
  }}}}
])

Après avoir ajouté le champ supplémentaire, vous pouvez continuer avec les étapes du pipeline et avoir les informations qui vous manquent

0voto

Jon Kern Points 796

Si vous recherchez la syntaxe mongoid (je l'utilise dans une application rails), voici ce que j'ai trouvé pour les utilisateurs d'une entreprise :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X