J'ai une collection T
, avec 2 champs: Grade1
et Grade2
, et je veux sélectionner ceux qui ont la condition Grade1 > Grade2
, comment puis-je obtenir une requête comme dans MySQL?
Select * from T Where Grade1 > Grade2
Vous pouvez utiliser un $ where. Sachez simplement que ce sera assez lent (doit exécuter du code Javascript sur chaque enregistrement), donc combinez-le avec les 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 réponse récente
Si votre requête ne contient que $where
== opérateur, vous pouvez ne transmettre que l'expression JavaScript:
db.T.find("this.Grade1 > this.Grade2");
Pour de meilleures performances, exécutez une opération d'agrégat qui a $redact
pipeline pour filtrer les documents qui satisfont à la condition donnée.
Le $redact
Le pipeline intègre les fonctionnalités de $project
et $match
pour implémenter la rédaction au niveau du champ où tous les documents correspondant à la condition seront renvoyés à l'aide de $$KEEP
et supprime des résultats de pipeline ceux qui ne correspondent pas à l'aide de $$Prune
variable.
L'exécution de l'opération d'agrégation suivante permet de filtrer les documents plus efficacement que d'utiliser $where
pour les grandes collections car il utilise un seul pipeline et des opérateurs natifs MongoDB, 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’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 (opérateur de version 3.6 mongo) pour utiliser les fonctions d'agrégation dans une requête standard.
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 les performances sont plus importantes que la lisibilité et tant que votre condition est constituée d'opérations arithmétiques simples, vous pouvez utiliser le pipeline d'agrégation. Commencez par utiliser $ project pour calculer le côté gauche de la condition (placez tous les champs sur le côté gauche). Puis utilisez $ match pour comparer avec une constante et un filtre. De cette façon, vous évitez l'exécution de javascript. Ci-dessous, 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, le meilleur de 1: 192 ms par boucle
Utiliser find et $ where:
%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))
1 boucle, meilleur de 1: 4,54 s par boucle