У меня есть коллекция T
с двумя полями: Grade1
и Grade2
, и я хочу выбрать те, у которых есть условие Grade1 > Grade2
, как я могу получить запрос, как в MySQL?
Select * from T Where Grade1 > Grade2
У меня есть коллекция T
с двумя полями: Grade1
и Grade2
, и я хочу выбрать те, у которых есть условие Grade1 > Grade2
, как я могу получить запрос, как в MySQL?
Select * from T Where Grade1 > Grade2
Ответы:
Вы можете использовать $ where. Просто имейте в виду, что это будет довольно медленно (необходимо выполнять код 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 mongo) для использования функций агрегирования в обычном запросе.
Сравнить 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. Ниже мой тест на питоне:
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 с на петлю