sql >> Database teknologi >  >> NoSQL >> MongoDB

Beregn antallet af indlejrede objekter med C#

Forespørgslen til at tælle de "unikke" forekomster i en "EndpointId" af hver af "Uid" i "Tags" og "Type" i "Sensors" ville være:

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

Eller til 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)
        }
      );

Hvilken udgang:

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

Selvom det faktisk er den "mest effektive" måde at gøre dette på, i betragtning af at de præsenterede dokumenter har unikke værdier for "Uid" alligevel ville være at $reduce beløbene i selve dokumenterne:

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

Udsagnet passer dog ikke rigtig godt til LINQ, så du skal bruge BsonDocument grænseflade til at bygge BSON for erklæringen. Og selvfølgelig hvor den samme "Uid" værdier "opstod" faktisk i flere dokumenter i samlingen, så $unwind sætninger er nødvendige for at "gruppere" dem sammen på tværs af dokumenter fra array-indgangene.

Original

Du løser dette ved at hente $size af arrays. For det ydre array gælder dette blot for feltstien for arrayet i dokumentet, og for de indre array-elementer skal du behandle med $map for at behandle hver "Tags" element, og få derefter $size af "Sensors" og $sum det resulterende array for at reducere til det samlede antal.

Per dokument ville det være:

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

Hvor du har tildelt klasser i din C#-kode ville være sådan:

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

Hvor de vender tilbage:

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

Hvor du vil $group resultaterne, som for eksempel over hele samlingen, så ville du gøre:

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

Hvilket for din C#-kode som før ville være:

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

Hvor de vender tilbage:

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

Og for "EndpointId , så bruger du blot det felt som grupperingsnøgle i stedet for null eller 0 som det bliver anvendt af C#-drivertilknytningen:

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

Hvilket selvfølgelig er den samme sum af de to dokumentprøver, du gav os:

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

Så disse er meget enkle resultater med simpel pipeline-udførelse, når du først har vænnet dig til syntaksen.

Jeg foreslår, at du gør dig bekendt med Aggregation Operators fra kernedokumentationen, og selvfølgelig "LINQ-snydeark" af udtryk og deres brugskortlægning fra C# Driver-kodelageret.

Se også den generelle LINQ-reference i C# Driver-referencen for andre eksempler på, hvordan dette er knyttet til Aggregation Framework af MongoDB generelt.




  1. Hvordan beregner man gennemsnit pr. dag felt ved hjælp af MongoDB?

  2. MongoDB Index Builds – Forhindrer brugere i at udløse nye Builds

  3. Model.find().toArray() hævder ikke at have .toArray()-metoden

  4. MongoDB:Misformet geo-forespørgsel med $geoIntersect på en polygon