У мене є колекція T
з 2 полями: Grade1
і Grade2
, і я хочу вибрати ті, що мають умову Grade1 > Grade2
, як я можу отримати запит, як у MySQL?
Select * from T Where Grade1 > Grade2
У мене є колекція T
з 2 полями: Grade1
і Grade2
, і я хочу вибрати ті, що мають умову Grade1 > Grade2
, як я можу отримати запит, як у MySQL?
Select * from T Where Grade1 > Grade2
Відповіді:
Ви можете використовувати $ де. Тільки врахуйте, що це буде досить повільно (має виконувати Javascript-код на кожному записі), тому поєднуйте з індексованими запитами, якщо можете.
db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );
або більш компактний:
db.T.find( { $where : "this.Grade1 > this.Grade2" } );
ви можете використовувати, $expr
як описано в останній відповіді
$where: function() { return this.Grade1 - this.Grade2 > variable }
?
db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});
він нічого не повертає, навіть один із докерів у колекції є ISODate("2017-01-20T10:55:08.000Z")
. Але <=
і, >=
здається, робота. будь-яка ідея?
this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
Ви можете використовувати $ expr (оператор версії 3,6 монго), щоб використовувати функції агрегації у звичайному запиті.
Порівняти query operators
проти aggregation comparison operators
.
Регулярний запит:
db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})
Запит агрегації:
db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
Якщо ваш запит складається лише з $where
оператора, ви можете передати лише вираз JavaScript:
db.T.find("this.Grade1 > this.Grade2");
Для більшої продуктивності запустіть сукупну операцію, яка має $redact
трубопровід для фільтрації документів, які відповідають заданій умові.
$redact
Трубопровід включає в себе функціональні можливості $project
і $match
реалізувати на рівні поля , де редакцію буде повертати всі документи , що відповідають умовам використання $$KEEP
і видаляє з результатів трубопроводів ті , які не збігаються з допомогою $$PRUNE
змінної.
Запуск наступної сукупної операції фільтрує документи ефективніше, ніж використовувати $where
для великих колекцій, оскільки для цього використовується один конвеєр і вбудовані оператори MongoDB, а не оцінки JavaScript $where
, які можуть уповільнити запит:
db.T.aggregate([
{
"$redact": {
"$cond": [
{ "$gt": [ "$Grade1", "$Grade2" ] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
яка є більш спрощеною версією включення двох трубопроводів $project
і $match
:
db.T.aggregate([
{
"$project": {
"isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] },
"Grade1": 1,
"Grade2": 1,
"OtherFields": 1,
...
}
},
{ "$match": { "isGrade1Greater": 1 } }
])
З MongoDB 3.4 та новішими версіями:
db.T.aggregate([
{
"$addFields": {
"isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] }
}
},
{ "$match": { "isGrade1Greater": 1 } }
])
У випадку, якщо продуктивність важливіша за читабельність, і якщо ваш стан складається з простих арифметичних операцій, ви можете використовувати конвеєрний конвеєр. Спочатку використовуйте $ project, щоб обчислити ліву частину умови (відведіть усі поля до лівої частини). Потім використовуйте $ match для порівняння з константою та фільтром. Таким чином ви уникаєте виконання JavaScript. Нижче мій тест на 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)
Використання агрегату:
%timeit -n1 -r1 list(coll.aggregate([
{
'$project': {
'diff': {'$subtract': ['$Grade1', '$Grade2']},
'Grade1': 1,
'Grade2': 1
}
},
{
'$match': {'diff': {'$gt': 0}}
}
]))
1 петля, найкраще 1: 192 мс на цикл
Використовуючи find і $ where:
%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))
1 петля, найкраще 1: 4,54 с на петлю