MongoDb condition de requête sur la comparaison de 2 champs
j'ai une collection T
, avec 2 champs: Grade1
et Grade2
, et je veux sélectionner ceux avec la condition Grade1 > Grade2
, Comment puis-je obtenir une requête comme dans MySQL?
Select * from T Where Grade1 > Grade2
4 réponses
Vous pouvez utiliser un $où. Juste être conscient qu'il sera assez lent (doit exécuter le code Javascript sur chaque enregistrement) donc combiner 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" } );
UPD for mongodb v. 3.6 +
vous pouvez utiliser $expr
comme décrit dans réponse récente
si votre requête se compose uniquement de la $where
opérateur, vous pouvez passer seulement L'expression JavaScript:
db.T.find("this.Grade1 > this.Grade2");
$redact
canalisation pour filtrer les documents qui satisfont à la condition donnée.
le $redact
pipeline incorpore la fonctionnalité de $project
et $match
pour mettre en œuvre la rédaction au niveau du champ où il retournera tous les documents correspondant à la condition en utilisant $$KEEP
et supprime du pipeline les résultats qui ne correspondent pas en utilisant le $$PRUNE
variable.
L'exécution du fonctionnement d'ensemble suivant filtrent les documents plus efficacement que l'utilisation $where
pour les grandes collections, car cela utilise un pipeline simple et les opérateurs MongoDB natifs, plutôt que des évaluations JavaScript avec $where
, qui peut ralentir la requête:
db.T.aggregate([
{
"$redact": {
"$cond": [
{ "$gt": [ "$Grade1", "$Grade2" ] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
qui est une version simplifiée de l'incorporation 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 } }
])
vous pouvez utiliser $expr (3.6 Mongo version operator ) pour utiliser des fonctions d'agrégation dans la requête régulière.
Compare query operators
vs aggregation comparison operators
.
Requête Régulière:
db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})
Agrégation De Requête:
db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
dans le cas où la performance est plus importante que la lisibilité et aussi longtemps que votre condition se compose d'opérations arithmétiques simples, vous pouvez utiliser pipeline d'agrégation. Tout d'abord, utilisez $projet pour calculer le côté gauche de la condition (prendre tous les champs à gauche). Ensuite, utilisez $correspond à comparer avec une constante et un filtre. Vous éviterez ainsi l'exécution 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)
utilisant l'agrégat:
%timeit -n1 -r1 list(coll.aggregate([
{
'$project': {
'diff': {'$subtract': ['$Grade1', '$Grade2']},
'Grade1': 1,
'Grade2': 1
}
},
{
'$match': {'diff': {'$gt': 0}}
}
]))
1 boucle, le meilleur de 1: 192 ms par boucle
utilisant find et $ where:
%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))
1 boucle, le meilleur de 1: 4.54 s par boucle