sql >> Database >  >> NoSQL >> MongoDB

Bereken het aantal geneste objecten met C#

De query om de "unieke" voorvallen te tellen binnen een "EndpointId" van elk van de "Uid" in "Tags" en de "Type" in "Sensors" zou zijn:

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" }
  }}
])

Of voor 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)
        }
      );

Welke uitgangen:

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

Hoewel dit eigenlijk de "meest efficiënte" manier is om dit te doen, aangezien de gepresenteerde documenten unieke waarden hebben voor "Uid" hoe dan ook zou zijn om $reduce de bedragen in de documenten zelf:

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" ] }
                }
              }
            }
          }
        }
      }
    }
  }}
])

De instructie komt echter niet echt goed overeen met LINQ, dus u zou de BsonDocument moeten gebruiken interface om de BSON voor de instructie te bouwen. En natuurlijk waar dezelfde "Uid" waarden "deden" in feite voorkomen in meerdere documenten in de verzameling, dan is de $unwind statements zijn nodig om deze te "groeperen" over documenten vanuit de array-items.

Origineel

U lost dit op door de $size te verkrijgen van de arrays. Voor de buitenste array is dit gewoon van toepassing op het veldpad van de array in het document, en voor de binnenste array-items moet je verwerken met $map om elke "Tags" . te verwerken element en verkrijg vervolgens de $size van "Sensors" en $sum de resulterende array te reduceren tot de totale telling.

Per document zou dat zijn:

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

Waar je klassen in je C#-code aan hebt toegewezen, zou er als volgt uitzien:

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

Waar die terugkeren:

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

Waar je naartoe wilt $group de resultaten, zoals bijvoorbeeld over de hele collectie, dan zou je het volgende doen:

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" }
          }
        }
      }
    }
  }}
])

Wat voor uw C#-code zoals voorheen zou zijn:

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())
    }
  );

Waar die terugkeren:

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

En voor "EndpointId , dan gebruik je dat veld gewoon als de groeperingssleutel, in plaats van de null of 0 zoals het wordt toegepast door de C#-stuurprogrammatoewijzing:

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())
    }
  );

Wat natuurlijk dezelfde som is van de twee documentvoorbeelden die u ons gaf:

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

Dit zijn dus heel eenvoudige resultaten, met eenvoudige pijplijnuitvoering als je eenmaal aan de syntaxis gewend bent.

Ik raad aan om vertrouwd te raken met de Aggregatie-operators uit de kerndocumentatie, en natuurlijk de "LINQ Cheatsheet" van uitdrukkingen en hun gebruikstoewijzing vanuit de C# Driver-coderepository.

Zie ook de algemene LINQ Reference in de C# Driver-referentie voor andere voorbeelden van hoe dit in het algemeen op het Aggregation Framework van MongoDB wordt weergegeven.




  1. Object.keys, hoe krijg je een lijst met sleutels in mongodb

  2. Spring Redis-fouthandvat

  3. MongoDB Aggregation Framework - Hoe matchen op datumbereik, groeperen op dagen en gemiddeld rendement voor elke dag?

  4. JSON opslaan en opvragen vanuit een database