sql >> データベース >  >> NoSQL >> MongoDB

配列からの一致条件と最新の日付

    ここでの基本的な概念は、条件を適用して配列要素を条件に「フィルタリング」するために、集約フレームワークが必要であるということです。利用可能なバージョンに応じて、適用できるさまざまな手法があります。

    すべての場合において、これは結果です:

    {
        "_id" : ObjectId("593921425ccc8150f35e7664"),
        "user1" : 1,
        "user2" : 4,
        "messages" : {
                "sender" : 1,
                "datetime" : ISODate("2017-06-09T10:04:50Z"),
                "body" : "hiii 1"
        }
    }
    {
        "_id" : ObjectId("593921425ccc8150f35e7663"),
        "user1" : 1,
        "user2" : 3,
        "messages" : {
                "sender" : 1,
                "datetime" : ISODate("2017-06-10T10:04:50Z"),
                "body" : "hiii 2"
        }
    }
    {
        "_id" : ObjectId("593921425ccc8150f35e7662"),
        "user1" : 1,
        "user2" : 2,
        "messages" : {
                "sender" : 1,
                "datetime" : ISODate("2017-06-08T10:04:50Z"),
                "body" : "hiii 0"
        }
    }
    

    MongoDB3.4以降

    db.chat.aggregate([
      { "$match": { "messages.sender": 1 } },
      { "$replaceRoot": {
        "newRoot": {
          "$let": {
            "vars": {
              "messages": {
                "$filter": {
                  "input": "$messages",
                  "as": "m",
                  "cond": { "$eq": [ "$$m.sender", 1 ] }
                }
              },
              "maxDate": {
                "$max": {
                  "$map": {
                    "input": {
                      "$filter": {
                        "input": "$messages",
                        "as": "m",
                        "cond": { "$eq": [ "$$m.sender", 1 ] }
                      }
                    },
                    "as": "m",
                    "in": "$$m.datetime"
                  }
                }
              }
            },
            "in": {
              "_id": "$_id",
              "user1": "$user1",
              "user2": "$user2",
              "messages": {
                "$arrayElemAt": [
                  { "$filter": {
                    "input": "$$messages",
                    "as": "m",
                    "cond": { "$eq": [ "$$m.datetime", "$$maxDate" ] }
                  }},
                  0
                ]
              }    
            }
          }
        }
      }}
    ])
    

    これは、 $replaceRoot<を利用する最も効率的な方法です。 / code> これにより、 <を使用して、「置換」構造で使用する変数を宣言できます。 code> $ let 。ここでの主な利点は、「2つの」パイプラインステージのみが必要なことです。

    配列のコンテンツを一致させるには、 $ filter $ eqを適用する場所 "sender"の値をテストするための論理演算 。条件が一致する場合、一致する配列エントリのみが返されます。

    同じ $ filterを使用する 一致する「送信者」エントリのみが考慮されるように、 $ max 「フィルタリングされた」リストを「datetime」の値に変更します 。 $ max ] 5 値は、条件による「最新の」日付です。

    この値が必要なのは、後で「フィルター処理された」配列から返された結果をこの「maxDate」と比較できるようにするためです。これは、 "in"内で発生することです $ letのブロック ここで、フィルタリングされたコンテンツに対して以前に宣言された2つの「変数」と「maxDate」が再び $ filter 「最新の日付」を持つ両方の条件を満たす唯一の値を返すため。

    「1つの」結果のみが必要なため、 $arrayElemAtを使用します。 配列ではなく値を使用します。

    MongoDB 3.2

    db.chat.aggregate([
      { "$match": { "messages.sender": 1 } },
      { "$project": {
        "user1": 1,
        "user2": 1,
        "messages": {
          "$filter": {
            "input": "$messages",
            "as": "m",
            "cond": { "$eq": [ "$$m.sender", 1 ] }
          }
        },
        "maxDate": {
          "$max": {
            "$map": {
              "input": {
                "$filter": {
                  "input": "$messages",
                  "as": "m",
                  "cond": { "$eq": [ "$$m.sender", 1 ] }
                }
              },
              "as": "m",
              "in": "$$m.datetime"
            }
          }
        }         
      }},
      { "$project": {
        "user1": 1,
        "user2": 1,
        "messages": {
          "$arrayElemAt":[
           { "$filter": {
             "input": "$messages",
              "as": "m",
              "cond": { "$eq": [ "$$m.datetime", "$maxDate" ] }
           }},
           0
          ]
        }
      }}
    ])
    

    これは基本的に説明と同じプロセスですが、$がありません。 replaceRoot パイプラインステージでは、2つの $ project ステージ。この理由は、最終的な $ filter 、および複合ステートメントで実行することはできないため、代わりにパイプラインを分割します。これは、運用の全体的なコストにわずかな影響を及ぼします。

    MongoDB 2.6から3.0では、 <を除いて、ここでほとんどの手法を使用できます。 code> $ arrayElemAt そして、単一のエントリで「配列」の結果を受け入れるか、 $ unwind 単一のエントリになるはずの段階に対処します。

    MongoDB以前のバージョン

    db.chat.aggregate([
      { "$match": { "messages.sender": 1 } },
      { "$unwind": "$messages" },
      { "$match": { "messages.sender": 1 } },
      { "$sort": { "_id": 1, "messages.datetime": -1 } },
      { "$group": {
        "_id": "$_id",
        "user1": { "$first": "$user1" },
        "user2": { "$first": "$user2" },
        "messages": { "$first": "$messages" }
      }}
    ])
    

    簡単に見えますが、これははるかにコストのかかる操作です。ここでは、 $ unwindを使用する必要があります 配列要素に条件を適用するため。これは、配列エントリごとに各ドキュメントのコピーを生成するため、非常にコストのかかるプロセスであり、「フィルタリング」の場合にこれを回避する最新の演算子に本質的に置き換えられます。

    2番目の $ match ここでのステ​​ージは、「送信者」条件に一致しなかった要素(現在は「ドキュメント」)を破棄します。次に、 $ sortを適用します。 _id によって、各ドキュメントの「最新」の日付を一番上に表示するため したがって、2つの「ソート」キー。

    最後に、 $ groupを適用します 元のドキュメントを参照するには、 $firstを使用します。 「上」にある要素を取得するためのアキュムレータとして。




    1. MongoDBのセキュリティレベルとそれらを回避する方法

    2. MongoDB:それぞれの異なる値がいくつあるかを数えますか?

    3. アトミックが存在しない場合は、Mongodb.Netドライバーに挿入します

    4. JunitでRedisをスキップ/モックする