web-dev-qa-db-ja.com

MongoDBelemmatch配列内の複数の要素

私は次のようなmongodbドキュメントを持っています

    {
        "_id" : ObjectId("54e66b2da7b5f3a92e09dc6c"),
        "SomeMetric" : [ 
            {
                //some object
            }
            {
                //some object
            } 
         ],
        "FilterMetric" : [ 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s1"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s2"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s1"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s2"
            } 
        ]
    }

通常、このようなネストされた配列が多数含まれています。検索条件を持つ配列のみを使用して、1つのメトリックのみを投影したいと思います。クエリがあります

db.sample.find(
{"filtermetric.class" : "s2"},{"filtermetric" : { $elemMatch : {class: "s2"}}}
)

これにより、配列の最初のオブジェクトのみが表示されます。クラス:s2の2番目のオブジェクトは返されません。

やってみたら

    db.sample.find(
   {"filtermetric" : { $elemMatch : {class: "s2"}}}
    )

配列内の4つのオブジェクトすべてが表示されます。

このような場合、基準に一致するすべてのオブジェクトを取得するにはどうすればよいですか?

11
Manoj

基本的な.find()クエリの形式では、条件に一致する配列の複数の要素を返すことはできません。複数の要素を照合するには、代わりに .aggregate() メソッドを使用する必要があります。

ここでの主な違いは、「クエリ」が意図したとおりに実行され、条件を満たす「ドキュメント」と一致することです。射影引数内で positional $ 演算子を使用することを試みることができますが、クエリ条件に一致する「最初の」配列要素にのみ一致するという規則があります。

複数の配列要素を「フィルタリング」するには、次の手順に従います。

db.sample.aggregate([
    // Filter possible documents
    { "$match": { "filtermetric.class": "s2" } },

    // Unwind the array to denormalize
    { "$unwind": "$filtermetric" },

    // Match specific array elements
    { "$match": { "filtermetric.class": "s2" } },

    // Group back to array form
    { "$group": {
        "_id": "$_id",
        "filtermetric": { "$Push": "$filtermetric" }
    }}
])

バージョン2.6以降のMongoDBの最新バージョンでは、これを $redact :で実行できます。

db.sample.aggregate([
    // Filter possible documents
    { "$match": { "filtermetric.class": "s2" } },

    // Redact the entries that do not match
    { "$redact": {
        "$cond": [
            { "$eq": [ { "$ifNull": [ "$class", "s2" ] }, "s2" ] },
            "$$DESCEND",
            "$$Prune"
        ]
    }}
])

これはおそらく最も効率的なオプションですが、再帰的であるため、同じ名前のフィールドが他のどのレベルの条件でも存在できないため、最初にドキュメント構造を検討してください。

おそらくより安全ですが、配列の結果が「本当に一意」である場合にのみ役立つのは、 $map および $setDifference

db.sample.aggregate([
    { "$project": {
        "filtermetric": { "$setDifference": [
            { "$map": [
                "input": "$filtermetric",
                "as": "el",
                "in": {"$cond": [
                    { "$eq": [ "$$el.class", "s2" ] },
                    "$$el",
                    false
                ]}
            ]},
            [false]
        ]}
    }}
])

また、 $group$project 運用パイプラインステージでは、そのステージから結果ドキュメントに返す予定のすべてのフィールドを指定する必要があります

最後の注意点は、配列内の単一のキーの値のみをクエリする場合は、 $elemMatch は必要ないということです。 "ドット表記" は、配列の単一のキーにのみアクセスする場合に推奨されます。 $elemMatchは、配列「element」内のドキュメント内の「複数の」キーがクエリ条件に一致する必要がある場合にのみ必要です。

18
Neil Lunn