Состояние в MongoDB запрос на сравнение 2 поля


у меня есть коллекция T С 2 полями: Grade1 и Grade2 и я хочу, чтобы выбрать те, с условием Grade1 > Grade2, как я могу получить запрос, как в MySQL?

Select * from T Where Grade1 > Grade2
4 77

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 конвейер для фильтрации документов, удовлетворяющих данному условию.

The $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 } }
])

можно использовать $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)

использование совокупности:

%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 s за цикл