J'ai une collection T
, avec 2 champs: Grade1
et Grade2
, et je veux sélectionner ceux avec condition Grade1 > Grade2
, comment puis-je obtenir une requête comme dans MySQL?
Select * from T Where Grade1 > Grade2
J'ai une collection T
, avec 2 champs: Grade1
et Grade2
, et je veux sélectionner ceux avec condition Grade1 > Grade2
, comment puis-je obtenir une requête comme dans MySQL?
Select * from T Where Grade1 > Grade2
Réponses:
Vous pouvez utiliser un $ where. Sachez simplement que ce sera assez lent (doit exécuter du code Javascript sur chaque enregistrement), alors combinez-le avec des requêtes indexées si vous le pouvez.
db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );
ou plus compact:
db.T.find( { $where : "this.Grade1 > this.Grade2" } );
vous pouvez utiliser $expr
comme décrit dans la réponse récente
$where: function() { return this.Grade1 - this.Grade2 > variable }
?
db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});
il ne renvoie rien, même l'un des doc de la collection l'est ISODate("2017-01-20T10:55:08.000Z")
. Mais <=
et >=
semblent fonctionner. une idée?
this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
Vous pouvez utiliser $ expr (opérateur de version 3.6 mongo) pour utiliser les fonctions d'agrégation dans une requête normale.
Comparez query operators
vs aggregation comparison operators
.
Requête régulière:
db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})
Requête d'agrégation:
db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
Si votre requête consiste uniquement en $where
opérateur, vous pouvez transmettre uniquement l'expression JavaScript:
db.T.find("this.Grade1 > this.Grade2");
Pour de meilleures performances, exécutez une opération d'agrégation avec un $redact
pipeline pour filtrer les documents qui satisfont à la condition donnée.
Le $redact
pipeline incorpore la fonctionnalité de $project
et $match
pour implémenter la rédaction au niveau du champ où il renverra tous les documents correspondant à la condition en utilisant$$KEEP
et supprime des résultats du pipeline ceux qui ne correspondent pas à l'aide de la $$PRUNE
variable.
L'exécution de l'opération d'agrégation suivante filtre les documents plus efficacement qu'en utilisant $where
pour les grandes collections, car elle utilise un seul pipeline et des opérateurs MongoDB natifs, plutôt que des évaluations JavaScript avec $where
, ce qui peut ralentir la requête:
db.T.aggregate([
{
"$redact": {
"$cond": [
{ "$gt": [ "$Grade1", "$Grade2" ] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
qui est une version plus simplifiée de l'intégration des deux pipelines $project
et$match
:
db.T.aggregate([
{
"$project": {
"isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] },
"Grade1": 1,
"Grade2": 1,
"OtherFields": 1,
...
}
},
{ "$match": { "isGrade1Greater": 1 } }
])
Avec MongoDB 3.4 et plus récent:
db.T.aggregate([
{
"$addFields": {
"isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] }
}
},
{ "$match": { "isGrade1Greater": 1 } }
])
Dans le cas où les performances sont plus importantes que la lisibilité et tant que votre condition consiste en des opérations arithmétiques simples, vous pouvez utiliser un pipeline d'agrégation. Tout d'abord, utilisez $ project pour calculer le côté gauche de la condition (prenez tous les champs sur le côté gauche). Ensuite, utilisez $ match pour comparer avec une constante et un filtre. De cette façon, vous évitez l'exécution de javascript. Voici mon test en python:
import pymongo
from random import randrange
docs = [{'Grade1': randrange(10), 'Grade2': randrange(10)} for __ in range(100000)]
coll = pymongo.MongoClient().test_db.grades
coll.insert_many(docs)
Utilisation d'agrégat:
%timeit -n1 -r1 list(coll.aggregate([
{
'$project': {
'diff': {'$subtract': ['$Grade1', '$Grade2']},
'Grade1': 1,
'Grade2': 1
}
},
{
'$match': {'diff': {'$gt': 0}}
}
]))
1 boucle, meilleur de 1: 192 ms par boucle
En utilisant find et $ where:
%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))
1 boucle, meilleur de 1: 4,54 s par boucle