web-dev-qa-db-ja.com

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

コレクションTに2つのフィールドGrade1およびGrade2があり、条件Grade1 > Grade2を持つコレクションを選択したいのですが、MySQLのようなクエリを取得するにはどうすればよいですか?

Select * from T Where Grade1 > Grade2
91
Diego Cheng

$ 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を使用できます

111
Ian

クエリが $where 演算子のみで構成されている場合、JavaScript式のみを渡すことができます。

db.T.find("this.Grade1 > this.Grade2");

パフォーマンスを向上させるには、$redactパイプラインを持つ集約操作を実行して、指定された条件を満たすドキュメントをフィルタリングします。

$redactパイプラインには$projectの機能が組み込まれていますおよび$matchは、 を使用して条件に一致するすべてのドキュメントを返すフィールドレベルの墨消しを実装します$$KEEP およびパイプラインの結果から、$$Prune変数を使用して一致しない結果を削除します。


次の集約操作を実行すると、 $where を使用するよりも効率的にドキュメントをフィルタリングします。これは、単一のパイプラインとネイティブMongoDB演算子を使用するため、 $where を使用したJavaScript評価。クエリが遅くなる可能性があります。

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 } }
])
35
chridam

$ expr (3.6 mongo version operator)を使用して、通常のクエリで集計関数を使用できます。

比較 query operators vs aggregation comparison operators

通常のクエリ:

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

集計クエリ:

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

可読性よりもパフォーマンスが重要であり、条件が単純な算術演算で構成されている限り、集約パイプラインを使用できます。まず、$ 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秒

11
Sina