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

最大サブドキュメントを含むドキュメントを返す

    まず、人々がデータを使用して望ましい結果を生み出すことができる方法でデータを表示しましょう:

    { value: 1,
      _id: ObjectId('5cb9ea0c75c61525e0176f96'),
      name: 'Test',
      category: 'Development',
      subcategory: 'Programming Languages',
      status: 'Supported',
      description: 'Test',
      change:
       [ { version: 1,
           who: 'ATL User',
           when: new Date('2019-04-19T15:30:39.912Z'),
           what: 'Item Creation' },
         { version: 2,
           who: 'ATL Other User',
           when: new Date('2019-04-19T15:31:39.912Z'),
           what: 'Name Change' } ],
    }
    

    "when"に注意してください 日付は実際には異なるため、 $ max 価値とそれらは同じではありません。これで、ケースを実行できます

    ケース1-「特異な」 $maxを取得します

    ここでの基本的なケースは、 $ arrayElemAt> および $ indexOfArray 一致する $ maxを返す演算子 値:

    db.items.aggregate([
      { "$match": {
        "subcategory": "Programming Languages", "name": { "$exists": true }
      }}, 
      { "$addFields": {
        "change": {
          "$arrayElemAt": [
            "$change",
            { "$indexOfArray": [
              "$change.when",
              { "$max": "$change.when" }
            ]}
          ]
        }
      }}
    ])
    

    返品:

    {
            "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
            "value" : 1,
            "name" : "Test",
            "category" : "Development",
            "subcategory" : "Programming Languages",
            "status" : "Supported",
            "description" : "Test",
            "change" : {
                    "version" : 2,
                    "who" : "ATL Other User",
                    "when" : ISODate("2019-04-19T15:31:39.912Z"),
                    "what" : "Name Change"
            }
    }
    

    基本的に、 "$ max": "$ change.when" その値の配列内から「最大」である値を返します。次に、 $ indexOfArray 最初のを返します 一致するインデックスが見つかりました。その「インデックス」位置(実際には "when"の配列から 同じ順序で転置された値)は、 $arrayElemAtで使用されます。 "change"から「オブジェクト全体」を抽出します 指定されたインデックス位置の配列。

    ケース2-「複数」を返す $ max エントリ

    $ maxでもほぼ同じです。 、今回を除いて、 $ filter 複数の"possible"を返す $ maxに一致する値 値:

    db.items.aggregate([
      { "$match": {
        "subcategory": "Programming Languages", "name": { "$exists": true }
      }}, 
      { "$addFields": {
        "change": {
          "$filter": {
            "input": "$change",
            "cond": {
              "$eq": [ "$$this.when", { "$max": "$change.when" } ]
            }
          }       
        }
      }}
    ])
    

    返品:

    {
            "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
            "value" : 1,
            "name" : "Test",
            "category" : "Development",
            "subcategory" : "Programming Languages",
            "status" : "Supported",
            "description" : "Test",
            "change" : [
                    {
                            "version" : 2,
                            "who" : "ATL Other User",
                            "when" : ISODate("2019-04-19T15:31:39.912Z"),
                            "what" : "Name Change"
                    }
            ]
    }
    

    したがって、 $ max もちろん同じですが、今回はその演算子によって返される特異値が $ eq $ filter内の比較 。これにより、各配列要素が検査され、現在が確認されます。 "when" 値( "$$ this.when" )。 「等しい」 その後、要素が返されます。

    基本的に最初のアプローチと同じですが、 $ filterを除いて 「複数」を許可します 返される要素。したがって、すべて 同じ $ max 価値。

    ケース3-アレイのコンテンツを事前に並べ替えます。

    ここで、私が含めたデータの例(独自のデータから適合したものですが、実際の「最大」日付を使用)では、「最大」値が実際には最後であることに気付くかもしれません。 配列の値。これは、 $ push (デフォルト) "appends" 既存の配列コンテンツの最後まで。つまり、「新しい」 エントリは最後になる傾向があります アレイの。

    もちろんこれはデフォルトです 行動しますが、「かもしれない」理由は十分にあります。 それを変えたい。要するに、最高 「最新」を取得する方法 配列エントリは、実際には最初の要素を返すことです。 アレイから。

    実際に行う必要があるのは、「最新」を確認することだけです。 実際に最初に追加されます 最後ではなく 。 2つのアプローチがあります:

    1. $ positionを使用します 配列アイテムを「プリペンド」するには: これは、 $ pushの単純な修飾子です。 0を使用する 位置 常にフロントに追加するために :

      db.items.updateOne(
        { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") },
        { "$push": {
            "change": {
              "$each": [{
                "version": 3,
                "who": "ATL User",
                "when": new Date(),
                "what": "Another change"
              }],
              "$position": 0
            }
         }}
      )
      

      これにより、ドキュメントが次のように変更されます。

      {
          "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
          "value" : 1,
          "name" : "Test",
          "category" : "Development",
          "subcategory" : "Programming Languages",
          "status" : "Supported",
          "description" : "Test",
          "change" : [
                  {
                          "version" : 3,
                          "who" : "ATL User",
                          "when" : ISODate("2019-04-20T02:40:30.024Z"),
                          "what" : "Another change"
                  },
                  {
                          "version" : 1,
                          "who" : "ATL User",
                          "when" : ISODate("2019-04-19T15:30:39.912Z"),
                          "what" : "Item Creation"
                  },
                  {
                          "version" : 2,
                          "who" : "ATL Other User",
                          "when" : ISODate("2019-04-19T15:31:39.912Z"),
                          "what" : "Name Change"
                  }
          ]
      }
      

    これには、実際にすべての配列要素を事前に「反転」して、「最新」がすでに最前面にあるようにして、順序が維持されるようにする必要があることに注意してください。ありがたいことに、これは2番目のアプローチである程度カバーされています...

    1. $ sortを使用します $ push> そしてこれは、新しいアイテムが追加されるたびに実際にアトミックに「再ソート」するもう1つの修飾子です。通常の使用法は、 $eachの新しいアイテムと基本的に同じです。 上記のように、またはを適用するための「空の」配列ですら、 $ sort 既存のデータのみ:

      db.items.updateOne(
        { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") },
        { "$push": {
            "change": {
              "$each": [],
              "$sort": { "when": -1 } 
            }
         }}
      )
      

      結果:

      {
              "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
              "value" : 1,
              "name" : "Test",
              "category" : "Development",
              "subcategory" : "Programming Languages",
              "status" : "Supported",
              "description" : "Test",
              "change" : [
                      {
                              "version" : 3,
                              "who" : "ATL User",
                              "when" : ISODate("2019-04-20T02:40:30.024Z"),
                              "what" : "Another change"
                      },
                      {
                              "version" : 2,
                              "who" : "ATL Other User",
                              "when" : ISODate("2019-04-19T15:31:39.912Z"),
                              "what" : "Name Change"
                      },
                      {
                              "version" : 1,
                              "who" : "ATL User",
                              "when" : ISODate("2019-04-19T15:30:39.912Z"),
                              "what" : "Item Creation"
                      }
              ]
      }
      

      $push<を理解するのに1分かかる場合があります/ code> $ sort このような配列ですが、一般的な目的は、 Dateのようなプロパティを「変更」する配列に変更を加える場合です。 値が並べ替えられている場合は、そのようなステートメントを使用してこれらの変更を反映します。または、 $ sort> うまくいきましょう。

    では、なぜ"ストア" 配列はこのように順序付けられていますか?前述のように、最初が必要です 「最新」としてのアイテム 、そしてそれを返すクエリは単純に次のようになります:

    db.items.find(
      {
        "subcategory": "Programming Languages",
        "name": { "$exists": true }
      },
      { "change": { "$slice": 1 } }
    )
    

    返品:

    {
            "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
            "value" : 1,
            "name" : "Test",
            "category" : "Development",
            "subcategory" : "Programming Languages",
            "status" : "Supported",
            "description" : "Test",
            "change" : [
                    {
                            "version" : 3,
                            "who" : "ATL User",
                            "when" : ISODate("2019-04-20T02:40:30.024Z"),
                            "what" : "Another change"
                    }
            ]
    }
    

    したがって、 $ sense 次に、既知のインデックスによって配列アイテムを抽出するためだけに使用できます。技術的には、 -1を使用できます 最後を返すためにそこに とにかく配列の項目ですが、最新のものが最初である並べ替えにより、最後の変更が特定のユーザーによって行われたことの確認や、日付範囲の制約などの他の条件などの他のことが可能になります。つまり:

    db.items.find(
      {
        "subcategory": "Programming Languages",
        "name": { "$exists": true },
        "change.0.who": "ATL User",
        "change.0.when": { "$gt": new Date("2018-04-01") }
      },
      { "change": { "$slice": 1 } }
    )
    

    ここで、"change.-1.when"のようなものに注意してください。 は不正なステートメントです。これが基本的に配列を並べ替えてlegalを使用できるようにする理由です。 0 最初の -1の代わりに 最後の場合 。

    結論

    したがって、配列コンテンツをフィルタリングするための集計アプローチを使用するか、データの実際の保存方法に変更を加えた後の標準のクエリフォームを使用して、実行できるさまざまなことがあります。どちらを使用するかは、ご自身の状況によって異なりますが、標準のクエリフォームのいずれかであることに注意してください。 集計フレームワークや計算された演算子を介した操作よりも著しく高速に実行されます。




    1. マングースの別のスキーマを参照する

    2. 素晴らしいMongoDBGUIツールの定義を支援する

    3. アプリは開発中は機能しますが、herokuにデプロイした場合は機能しません

    4. Mongo Map-SQLでcount(distinct(...))groupbyを模倣するように削減