Условие запроса MongoDb при сравнении 2 полей
у меня есть коллекция T
С 2 полями: Grade1
и Grade2
и я хочу, чтобы выбрать те, с условием Grade1 > Grade2
, как я могу получить запрос, как в MySQL?
Select * from T Where Grade1 > Grade2
4 ответов
вы можете использовать $where. Просто имейте в виду, что это будет довольно медленно (должен выполнять код Javascript на каждой записи), поэтому объедините с индексированными запросами, если сможете.
db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );
или более компактно:
db.T.find( { $where : "this.Grade1 > this.Grade2" } );
UPD для mongodb V. 3.6+
можно использовать $expr
как описано в недавний ответ
если ваш запрос состоит только из $where
оператор, вы можете передать только выражение JavaScript:
db.T.find("this.Grade1 > this.Grade2");
для повышения производительности запустите агрегатную операцию, которая имеет $redact
конвейер для фильтрации документов, удовлетворяющих заданному условию.
на $redact
трубопровод включает функциональность $project
и $match
для реализации редактирования уровня поля, где он вернет все документы, соответствующие условию, используя $$KEEP
и удаляет из результатов конвейера те, которые не соответствуют с помощью $$PRUNE
переменной.
выполнение следующей операции aggregate фильтрует документы более эффективно, чем использование $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 } }
])
можно использовать $expr (оператор версии 3.6 mongo) для использования функций агрегации в регулярном запросе.
сравнить query operators
vs aggregation comparison operators
.
Регулярный Запрос:
db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})
Запрос Агрегации:
db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
в случае, если производительность более важна, чем читаемость, и пока ваше условие состоит из простых арифметических операций, вы можете использовать конвейер агрегации. Во-первых, используйте $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)
использование aggregate:
%timeit -n1 -r1 list(coll.aggregate([
{
'$project': {
'diff': {'$subtract': ['$Grade1', '$Grade2']},
'Grade1': 1,
'Grade2': 1
}
},
{
'$match': {'diff': {'$gt': 0}}
}
]))
1 петля, лучше всего 1: 192 ms за цикл
использование find и $where:
%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))
1 петля, самое лучшее 1: 4.54 s в петлю