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
77
demandé sur alaster 2010-12-14 21:00:37

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

100
répondu Ian 2018-02-03 01:34:53

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 } }
])
31
répondu chridam 2017-02-06 17:38:47

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"]}}})
14
répondu Veeram 2018-02-21 17:02:04

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

11
répondu Sina 2016-07-05 17:27:35