Mam kolekcję T
z 2 polami: Grade1
i Grade2
chcę wybrać te z warunkiem Grade1 > Grade2
, jak mogę uzyskać zapytanie, takie jak w MySQL?
Select * from T Where Grade1 > Grade2
Mam kolekcję T
z 2 polami: Grade1
i Grade2
chcę wybrać te z warunkiem Grade1 > Grade2
, jak mogę uzyskać zapytanie, takie jak w MySQL?
Select * from T Where Grade1 > Grade2
Odpowiedzi:
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" } );
możesz użyć $expr
zgodnie z opisem w najnowszej odpowiedzi
$where: function() { return this.Grade1 - this.Grade2 > variable }
?
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ł?
this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
Możesz użyć $ expr (operator wersji 3.6 mongo), aby użyć funkcji agregujących w zwykłym zapytaniu.
Porównaj query operators
z aggregation comparison operators
.
Regularne zapytanie:
db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})
Zapytanie agregujące:
db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
Jeśli zapytanie składa się tylko z $where
operatora, 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.
$redact
Rurociąg łączy funkcjonalność $project
i $match
wdrożyć szczeblu terenowym redakcji, gdzie będzie powrotu wszystkich dokumentów pasujących warunek korzystania $$KEEP
i usuwa z wyników rurociągowych te, które nie pasują do siebie za pomocą $$PRUNE
zmiennej.
Uruchomienie następującej operacji agregacji $where
powoduje 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 $project
i$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 } }
])
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ę