2つのフィールドを比較する際のMongoDbクエリ条件


108

私は、コレクション持つT:2つのフィールドとを、Grade1そしてGrade2、私は条件を有するものを選択したいGrade1 > Grade2私は、MySQLのようにクエリを取得する方法、?

Select * from T Where Grade1 > Grade2

回答:


120

$ whereを使用できます。かなり遅い(すべてのレコードでJavascriptコードを実行する必要がある)ので、可能であればインデックス付きクエリと組み合わせてください。

db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );

以上のコンパクト:

db.T.find( { $where : "this.Grade1 > this.Grade2" } );

mongodb v.3.6以降のUPD

最近の回答で$expr説明されているように使用できます


おっと、わかった。JavaScriptや他のシェルスクリプトを一緒に使用するだけだ。
ディエゴチェン

16
これをもう少しコンパクトにすることもできます...> db.T.find({$ where: "this.Grade1> this.Grade2"});
Justin Jenkins

どうすればいい $where: function() { return this.Grade1 - this.Grade2 > variable }ですか?
ルイス・ゴンサレス

私がdb.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});それを試みたときに何も返さない、コレクション内のドキュメントの1つでさえもですISODate("2017-01-20T10:55:08.000Z")。しかし<=>=仕事のようです。何か案が?
cateyes 2017

@cateyes少し遅いかもしれませんが、2つの日付間で==比較を行うと、純粋なJavaScriptは常にfalseを返します。ただし、Mongoでは、クエリで日付間の完全一致を検索できます。回避策の一つは、ミリ秒または何に変換し.getTimeを()を使用している:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC

53

あなたは使用することができます$ exprの定期的なクエリで集計関数を使用する(3.6のmongoバージョン演算子を)。

比較query operatorsaggregation 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、大規模なコレクションを使用するよりも効率的にドキュメントをフィルタリング$whereできます。これは、によるJavaScript評価ではなく、単一のパイプラインとネイティブのMongoDB演算子を使用するため、クエリが遅くなる可能性があります。

db.T.aggregate([
    {
        "$redact": {
            "$cond": [
                { "$gt": [ "$Grade1", "$Grade2" ] },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])

これは2つのパイプラインを組み込んでいるのがより簡略化されたバージョンである$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の値に関係なくすべての行に一致します。この行動の原因は何でしょうか?編集:気にしないで、aggregate()に配列を渡さなかったのではなく、各集約自体をパラメーターとして、それを逃しました。
ThatBrianDude 2017

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 ms

検索と$ whereの使用:

%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))

1ループ、最高1:ループあたり4.54秒

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.