Условие запроса MongoDb при сравнении 2 полей - PullRequest
87 голосов
/ 14 декабря 2010

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

Select * from T Where Grade1 > Grade2

Ответы [ 4 ]

109 голосов
/ 14 декабря 2010

Вы можете использовать $ где. Просто имейте в виду, что это будет довольно медленно (должен выполнять код 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, как описано в недавний ответ

34 голосов
/ 26 марта 2015

Если ваш запрос состоит только из оператора $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 } }
])
28 голосов
/ 24 января 2018

Вы можете использовать $ expr (оператор версии 3,6 монго), чтобы использовать функции агрегирования в обычном запросе.

Сравнить query operators против aggregation comparison operators.

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

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

Запрос агрегации:

db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
11 голосов
/ 24 июня 2016

В случае, если производительность важнее, чем удобочитаемость, и если ваше условие состоит из простых арифметических операций, вы можете использовать конвейер агрегации.Сначала используйте $ 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 с на цикл

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...