Умова запиту MongoDb для порівняння 2 полів


108

У мене є колекція Tз 2 полями: Grade1і Grade2, і я хочу вибрати ті, що мають умову Grade1 > Grade2, як я можу отримати запит, як у MySQL?

Select * from T Where Grade1 > Grade2

Відповіді:


120

Ви можете використовувати $ де. Тільки врахуйте, що це буде досить повільно (має виконувати 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як описано в останній відповіді


На жаль, я розумію, просто спільне використання JavaScript або інші сценарії оболонки, дякую обом, хлопці!
Дієго Чен

16
Ви також можете зробити це трохи компактніше ...> db.T.find ({$ where: "this.Grade1> this.Grade2"});
Джастін Дженкінс

як я міг $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"). Але <=і, >=здається, робота. будь-яка ідея?
cateyes

@cateyes Можливо, трохи пізно ... але чистий JavaScript завжди повертає помилку, коли ви робите == порівняти між двома датами. Однак Mongo дозволяє шукати точні збіги між датами в запитах. Одне вирішення проблеми - використання .getTime () для перетворення в мілісекунди чи що завгодно:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC

53

Ви можете використовувати $ expr (оператор версії 3,6 монго), щоб використовувати функції агрегації у звичайному запиті.

Порівняти query operatorsпроти aggregation comparison operators.

Регулярний запит:

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

Запит агрегації:

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

39

Якщо ваш запит складається лише з $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 } }
])

останній, здається, не працює зі мною. Поле isGrade1Greater правильно додається та оцінюється, але чомусь запит відповідає всім рядкам незалежно від значення isGrade1Greater. Що може бути причиною такої поведінки? EDIT: Неважливо, я не передавав масив для агрегації (), але, скоріше, кожне агрегування як сам параметр, це пропустив.
ThatBrianDude

13

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.