Warunek zapytania MongoDb dotyczący porównania 2 pól

108

Mam kolekcję Tz 2 polami: Grade1i Grade2chcę wybrać te z warunkiem Grade1 > Grade2, jak mogę uzyskać zapytanie, takie jak w MySQL?

Select * from T Where Grade1 > Grade2
Diego Cheng
źródło

Odpowiedzi:

120

Możesz użyć znaku $ gdzie. Pamiętaj tylko, że będzie to dość powolne (musi wykonać kod Javascript na każdym rekordzie), więc jeśli możesz, połącz je z indeksowanymi zapytaniami.

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

lub bardziej zwarty:

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

UPD dla mongodb v.3.6 +

możesz użyć $exprzgodnie z opisem w najnowszej odpowiedzi

Ian
źródło
Ups, rozumiem, po prostu wspólnie używaj javascript lub innych skryptów powłoki, dziękuję wam obojgu!
Diego Cheng,
16
Możesz to zrobić trochę bardziej kompaktowo ...> db.T.find ({$ gdzie: "this.Grade1> this.Grade2"});
Justin Jenkins,
jak mogłem $where: function() { return this.Grade1 - this.Grade2 > variable }?
Luis González
kiedy próbowałem db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});, nic nie zwraca, nawet jeden z dokumentów w kolekcji to ISODate("2017-01-20T10:55:08.000Z"). Ale <=i >=wydaje się, że działa. dowolny pomysł?
cateyes
@cateyes Może trochę za późno ... ale czysty javascript zawsze zwraca fałsz podczas porównywania == między 2 datami. Mongo pozwala jednak wyszukiwać dokładne dopasowania między datami w zapytaniach. Jednym obejściem jest użycie .getTime () do konwersji na milisekundy lub cokolwiek innego:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC
53

Możesz użyć $ expr (operator wersji 3.6 mongo), aby użyć funkcji agregujących w zwykłym zapytaniu.

Porównaj query operatorsz aggregation comparison operators.

Regularne zapytanie:

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

Zapytanie agregujące:

db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
svr
źródło
39

Jeśli zapytanie składa się tylko z $whereoperatora, możesz przekazać tylko wyrażenie JavaScript:

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

Aby uzyskać większą wydajność, uruchom operację agregującą, która ma rozszerzenie $redact potok do filtrowania dokumentów spełniających podany warunek.

$redactRurociąg łączy funkcjonalność $projecti $matchwdrożyć szczeblu terenowym redakcji, gdzie będzie powrotu wszystkich dokumentów pasujących warunek korzystania $$KEEPi usuwa z wyników rurociągowych te, które nie pasują do siebie za pomocą $$PRUNEzmiennej.


Uruchomienie następującej operacji agregacji $wherepowoduje skuteczniejsze filtrowanie dokumentów niż używanie do dużych kolekcji, ponieważ wykorzystuje ona pojedynczy potok i natywne operatory MongoDB, a nie oceny JavaScript $where, co może spowolnić zapytanie:

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

który jest bardziej uproszczoną wersją włączenia dwóch rurociągów $projecti$match :

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

Z MongoDB 3.4 i nowszymi:

db.T.aggregate([
    {
        "$addFields": {
            "isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] }
        }
    },
    { "$match": { "isGrade1Greater": 1 } }
])
chridam
źródło
ten ostatni wydaje się nie działać ze mną. Pole isGrade1Greater jest poprawnie dodawane i oceniane, ale z jakiegoś powodu zapytanie pasuje do wszystkich wierszy bez względu na wartość isGrade1Greater. Jaka może być przyczyna takiego zachowania? EDYCJA: Nieważne, nie przekazywałem tablicy do agregacji (), ale raczej każdą agregację jako parametr, przegapiłem.
ThatBrianDude
13

W przypadku, gdy wydajność jest ważniejsza niż czytelność i jeśli twój warunek składa się z prostych operacji arytmetycznych, możesz użyć potoku agregacji. Najpierw użyj $ project do obliczenia lewej strony warunku (przenieś wszystkie pola do lewej strony). Następnie użyj $ match, aby porównać ze stałą i filtrem. W ten sposób unikniesz wykonywania javascript. Poniżej mój test w Pythonie:

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)

Korzystanie z agregatu:

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

1 pętla, najlepiej 1: 192 ms na pętlę

Korzystanie z funkcji find i $ where:

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

1 pętla, najlepiej 1: 4,54 s na pętlę

Sina
źródło