Условие запроса MongoDb при сравнении двух полей

У меня есть коллекция 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" } ); 

UPD для mongodb v.3.6 +

вы можете использовать $expr как описано в последнем ответе

Если ваш запрос состоит только из оператора $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 и $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 с за цикл

Вы можете использовать $ expr (оператор версии 3.6 mongo) для использования функций агрегации в регулярном запросе.

Сравните query operators сравнения aggregation comparison operators .

Обычный запрос:

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

Запрос агрегирования:

 db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}}) 
Давайте будем гением компьютера.