Frage MongoDb-Abfragebedingung beim Vergleich von 2 Feldern


Ich habe eine Sammlung Tmit 2 Feldern: Grade1 und Grade2und ich möchte diejenigen mit Bedingung auswählen Grade1 > Grade2, wie kann ich eine Abfrage wie in MySQL bekommen?

Select * from T Where Grade1 > Grade2

75
2017-12-14 18:00


Ursprung


Antworten:


Sie können ein $ wo verwenden. Seien Sie sich jedoch bewusst, dass es ziemlich langsam ist (Javascript-Code muss für jeden Datensatz ausgeführt werden). Kombinieren Sie dies mit indizierten Abfragen, wenn Sie können.

db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );

oder kompakter:

db.T.find( { $where : "this.Grade1 > this.Grade2" } );

UPD für mongodb v.3.6 +

Sie können verwenden $expr wie beschrieben in letzte Antwort


98
2017-12-14 19:43



Wenn Ihre Anfrage nur aus der besteht $where Operator, können Sie nur den JavaScript-Ausdruck übergeben:

db.T.find("this.Grade1 > this.Grade2");

Führen Sie für eine höhere Leistung eine Aggregatoperation mit a aus $redact Pipeline, um die Dokumente zu filtern, die die gegebene Bedingung erfüllen.

Das $redact Pipeline enthält die Funktionalität von $project und $match Um die Bearbeitung auf Feldebene zu implementieren, werden alle Dokumente zurückgegeben, die die Bedingung erfüllen $$KEEP und entfernt aus der Pipeline Ergebnisse, die nicht übereinstimmen, indem Sie die $$PRUNE Variable.


Wenn Sie die folgende Aggregatoperation ausführen, werden die Dokumente effizienter gefiltert als verwendet $where für große Sammlungen, da diese eine einzelne Pipeline und native MongoDB-Operatoren anstelle von JavaScript-Auswertungen mit verwendet $where, was die Abfrage verlangsamen kann:

db.T.aggregate([
    {
        "$redact": {
            "$cond": [
                { "$gt": [ "$Grade1", "$Grade2" ] },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])

Dies ist eine vereinfachte Version der Einbeziehung der beiden Pipelines $project und $match:

db.T.aggregate([
    {
        "$project": {
            "isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] },
            "Grade1": 1,
            "Grade2": 1,
            "OtherFields": 1,
            ...
        }
    },
    { "$match": { "isGrade1Greater": 1 } }
])

Mit MongoDB 3.4 und neuer:

db.T.aggregate([
    {
        "$addFields": {
            "isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] }
        }
    },
    { "$match": { "isGrade1Greater": 1 } }
])

31
2018-03-26 14:43



Wenn die Leistung wichtiger als die Lesbarkeit ist und Ihre Bedingung aus einfachen arithmetischen Operationen besteht, können Sie die Aggregationspipeline verwenden. Verwenden Sie zuerst $ project, um die linke Seite der Bedingung zu berechnen (nehmen Sie alle Felder zur linken Seite). Dann benutze $ match um mit einer Konstanten zu vergleichen und zu filtern. Auf diese Weise vermeiden Sie die Ausführung von JavaScript. Unten ist mein Test in 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)

Aggregat verwenden:

%timeit -n1 -r1 list(coll.aggregate([
    {
        '$project': {
            'diff': {'$subtract': ['$Grade1', '$Grade2']},
            'Grade1': 1,
            'Grade2': 1
        }
    },
    {
        '$match': {'diff': {'$gt': 0}}
    }
]))

1 Schleife, beste 1: 192 ms pro Schleife

Verwenden von find und $ wo:

%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))

1 Schleife, beste 1: 4,54 s pro Schleife


11
2018-06-23 21:08



Sie können verwenden $ expr (3.6 Mongo Version Operator) um Aggregationsfunktionen in regulären Abfragen zu verwenden.

Vergleichen query operators vs aggregation comparison operators.

Reguläre Abfrage:

db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})

Aggregationsabfrage:

db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})

10
2018-01-23 21:14