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

C#でネストされたオブジェクトの数を計算します

    "EndpointId"内の「一意の」発生をカウントするクエリ 各"Uid""Tags"で および"Type" "Sensors"で 次のようになります:

    db.collection.aggregate([
      { "$unwind": "$Tags" },
      { "$unwind": "$Tags.Sensors" },
      { "$group": {
        "_id": {
          "EndpointId": "$EndpointId",
          "Uid": "$Tags.Uid",
          "Type": "$Tags.Sensors.Type"
        },
      }},
      { "$group": {
        "_id": {
          "EndpointId": "$_id.EndpointId",
          "Uid": "$_id.Uid",
        },
        "count": { "$sum": 1 }
      }},
      { "$group": {
        "_id": "$_id.EndpointId",
        "tagCount": { "$sum": 1 },
        "sensorCount": { "$sum": "$count" }
      }}
    ])
    

    またはC#の場合

        var results = collection.AsQueryable()
          .SelectMany(p => p.Tags, (p, tag) => new
            {
              EndpointId = p.EndpointId,
              Uid = tag.Uid,
              Sensors = tag.Sensors
            }
          )
          .SelectMany(p => p.Sensors, (p, sensor) => new
            {
              EndpointId = p.EndpointId,
              Uid = p.Uid,
              Type = sensor.Type
            }
          )
          .GroupBy(p => new { EndpointId = p.EndpointId, Uid = p.Uid, Type = p.Type })
          .GroupBy(p => new { EndpointId = p.Key.EndpointId, Uid = p.Key.Uid },
            (k, s) => new { Key = k, count = s.Count() }
          )
          .GroupBy(p => p.Key.EndpointId,
            (k, s) => new
            {
              EndpointId = k,
              tagCount = s.Count(),
              sensorCount = s.Sum(x => x.count)
            }
          );
    

    どの出力:

    {
      "EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83",
      "tagCount" : 4,
      "sensorCount" : 16
    }
    

    提示されたドキュメントが"Uid"に固有の値を持っていることを考えると、実際にはこれを行うための「最も効率的な」方法ですが とにかく$reduce ドキュメント自体の金額:

    db.collection.aggregate([
      { "$group": {
        "_id": "$EndpointId",
        "tags": {
          "$sum": {
            "$size": { "$setUnion": ["$Tags.Uid",[]] }
          }
        },
        "sensors": {
          "$sum": {
            "$sum": {
              "$map": {
                "input": { "$setUnion": ["$Tags.Uid",[]] },
                "as": "tag",
                "in": {
                  "$size": {
                    "$reduce": {
                      "input": {
                        "$filter": {
                          "input": {
                            "$map": {
                              "input": "$Tags",
                              "in": {
                                "Uid": "$$this.Uid",
                                "Type": "$$this.Sensors.Type"
                              }
                            }
                          },
                          "cond": { "$eq": [ "$$this.Uid", "$$tag" ] }
                        }
                      },
                      "initialValue": [],
                      "in": { "$setUnion": [ "$$value", "$$this.Type" ] }
                    }
                  }
                }
              }
            }
          }
        }
      }}
    ])
    

    ただし、このステートメントはLINQに適切にマッピングされないため、BsonDocumentを使用する必要があります。 ステートメントのBSONを構築するためのインターフェース。そしてもちろん、同じ"Uid" 値「did」は実際にはコレクション内の複数のドキュメント内で発生し、次に$unwind ステートメントは、配列エントリ内からドキュメント全体でそれらを「グループ化」するために必要です。

    オリジナル

    これを解決するには、 $sizeを取得します。 アレイの。外側の配列の場合、これはドキュメント内の配列のフィールドパスに適用されるだけであり、内側の配列アイテムの場合は、 $map "Tags"を処理するため 要素を取得してから、 $sizeを取得します。 "Sensors"の および $sum 結果として得られる配列は、全体の数になります。

    ドキュメントごとに次のようになります:

    db.collection.aggregate([
      { "$project": {
        "tags": { "$size": "$Tags" },
        "sensors": {
          "$sum": {
            "$map": {
              "input": "$Tags",
               "in": { "$size": "$$this.Sensors" }
            }
          }
        }
      }}
    ])
    

    C#コードのクラスに割り当てた場所は次のようになります:

    collection.AsQueryable()
      .Select(p => new
        {
          tags = p.Tags.Count(),
          sensors = p.Tags.Select(x => x.Sensors.Count()).Sum()
        }
      );
    

    それらが戻る場所:

    { "tags" : 3, "sensors" : 13 }
    { "tags" : 2, "sensors" : 8 }
    

    $groupが必要な場所 結果は、たとえばコレクション全体で、次のようになります。

    db.collection.aggregate([
      /* The shell would use $match for "query" conditions */
      //{ "$match": { "EndpointId": "89799bcc-e86f-4c8a-b340-8b5ed53caf83" } },
      { "$group": {
        "_id": null,
        "tags": { "$sum": { "$size": "$Tags" } },
        "sensors": {
          "$sum": {
            "$sum": {
              "$map": {
                "input": "$Tags",
                 "in": { "$size": "$$this.Sensors" }
              }
            }
          }
        }
      }}
    ])
    

    以前のようなC#コードの場合:

    collection.AsQueryable()
      .GroupBy(p => "", (k,s) => new
        {
          tags = s.Sum(p => p.Tags.Count()),
          sensors = s.Sum(p => p.Tags.Select(x => x.Sensors.Count()).Sum())
        }
      );
    

    それらが戻る場所:

    { "tags" : 5, "sensors" : 21 }
    

    そして"EndpointId 、次に、nullではなく、そのフィールドをグループ化キーとして使用するだけです。 または0 C#ドライバーマッピングによって適用されるため:

    collection.AsQueryable()
      /* Use the Where if you want a query to match only those documents */
      //.Where(p => p.EndpointId == "89799bcc-e86f-4c8a-b340-8b5ed53caf83")            
      .GroupBy(p => p.EndpointId, (k,s) => new
        {
          tags = s.Sum(p => p.Tags.Count()),
          sensors = s.Sum(p => p.Tags.Select(x => x.Sensors.Count()).Sum())
        }
      );
    

    もちろん、これはあなたが私たちに提供した2つのドキュメントサンプルの同じ合計です:

    { "tags" : 5, "sensors" : 21 }
    

    したがって、これらは非常に単純な結果であり、構文に慣れると単純なパイプライン実行が行われます。

    AggregationOperators に精通することをお勧めします コアドキュメントから、そしてもちろん"LINQチートシート" C#ドライバーコードリポジトリを使用した式とその使用法のマッピングの比較。

    一般的なLINQリファレンス も参照してください。 これが一般的にMongoDBのアグリゲーションフレームワークにどのようにマッピングされるかについての他の例については、C#ドライバーリファレンスを参照してください。




    1. 既存のユーザーのMongoDBのパスワードを変更する

    2. MongoDBのGroupBy複数の列

    3. MongoDB:一括挿入(Bulk.insert)と複数挿入(insert([...]))

    4. PTVS、IronPython、MongoDBの操作