sql >> Databasteknik >  >> NoSQL >> MongoDB

Beräkna antalet kapslade objekt med C#

Frågan för att räkna de "unika" förekomsterna inom en "EndpointId" för var och en av "Uid" i "Tags" och "Type" i "Sensors" skulle vara:

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 för 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)
        }
      );

Vilka utgångar:

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

Fast faktiskt det "mest effektiva" sättet att göra detta med tanke på att de presenterade dokumenten har unika värden för "Uid" hur som helst skulle vara att $reduce beloppen i själva dokumenten:

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

Uttrycket passar dock inte riktigt bra till LINQ, så du skulle behöva använda BsonDocument gränssnitt för att bygga BSON för uttalandet. Och naturligtvis där samma "Uid" värden "förekom" faktiskt i flera dokument i samlingen, sedan $unwind uttalanden är nödvändiga för att "gruppera" dessa över dokument från arrayposterna.

Original

Du löser detta genom att skaffa $size av arrayerna. För den yttre arrayen gäller detta helt enkelt för fältsökvägen för arrayen i dokumentet, och för de inre arrayobjekten måste du bearbeta med $map för att bearbeta varje "Tags" element och hämta sedan $size av "Sensors" och $sum den resulterande matrisen för att reducera till det totala antalet.

Per dokument skulle det vara:

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

Vilket där du har tilldelat klasser i din C#-kod skulle vara som:

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

Vart de kommer tillbaka:

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

Där du vill $group resultaten, som till exempel över hela samlingen, då skulle du göra:

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

Vilket för din C#-kod som tidigare skulle vara:

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

Vart de kommer tillbaka:

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

Och för "EndpointId , då använder du helt enkelt det fältet som grupperingsnyckel, snarare än null eller 0 när det appliceras av C#-drivrutinen:

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

Vilket naturligtvis är samma summa av de två dokumentexemplen du gav oss:

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

Så det här är väldigt enkla resultat, med enkel pipelineexekvering när du väl har vant dig vid syntaxen.

Jag föreslår att du bekantar dig med Aggregationsoperatörerna från kärndokumentationen, och naturligtvis "LINQ Cheat Sheet" av uttryck och deras användningskartläggning från C# Driver-kodförrådet.

Se även den allmänna LINQ-referensen i C# Driver-referensen för andra exempel på hur detta mappar till MongoDBs Aggregation Framework i allmänhet.




  1. Hur ser man eventuella ändringar (ny rad) i mongoDB?

  2. Mongoose får dokument som matchar arrayen

  3. Store enum MongoDB

  4. Redis:Race Condition och enkelgängad