J'ai une collection T, avec 2 champs: Grade1et 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: Grade1et 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 $exprcomme 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 operatorsvs 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 $redactpipeline pour filtrer les documents qui satisfont à la condition donnée.
Le $redactpipeline incorpore la fonctionnalité de $projectet $matchpour 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 $$PRUNEvariable.
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 $projectet$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