sql >> Base de Datos >  >> NoSQL >> MongoDB

Calcule el recuento de objetos anidados con C#

La consulta para contar las ocurrencias "únicas" dentro de un "EndpointId" de cada uno de los "Uid" en "Tags" y el "Type" en "Sensors" sería:

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

O para 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)
        }
      );

Qué salidas:

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

Aunque en realidad es la forma "más eficiente" de hacer esto considerando que los documentos presentados tienen valores únicos para "Uid" de todos modos sería $reduce las cantidades dentro de los propios documentos:

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

Sin embargo, la declaración realmente no se corresponde bien con LINQ, por lo que se le pedirá que use el BsonDocument interfaz para construir el BSON para la declaración. Y, por supuesto, donde el mismo "Uid" los valores "ocurrieron" de hecho dentro de múltiples documentos en la colección, entonces el $unwind las declaraciones son necesarias para "agruparlas" en los documentos desde las entradas de la matriz.

Original

Resuelve esto obteniendo el $size de las matrices. Para la matriz externa, esto simplemente se aplica a la ruta de campo de la matriz en el documento, y para los elementos de la matriz interna, debe procesar con $map para procesar cada "Tags" y luego obtenga el $size de "Sensors" y $sum la matriz resultante para reducirla al recuento total.

Por documento sería:

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

Que donde haya asignado clases en su código C# sería como:

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

Donde esos regresan:

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

Donde desea $group los resultados, como por ejemplo sobre toda la colección, entonces haría:

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

Que para su código C# como antes sería:

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

Donde esos regresan:

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

Y para "EndpointId , simplemente use ese campo como clave de agrupación, en lugar de null o 0 como lo aplica la asignación del controlador 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())
    }
  );

Que es, por supuesto, la misma suma de los dos documentos de muestra que nos diste:

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

Estos son resultados muy simples, con una ejecución de canalización simple una vez que te acostumbras a la sintaxis.

Le sugiero que se familiarice con los Operadores de agregación de la documentación principal y, por supuesto, el "Hoja de referencia de LINQ" de expresiones y su asignación de uso desde el repositorio de código del controlador C#.

Consulte también la referencia de LINQ general. en la referencia del controlador C# para ver otros ejemplos de cómo esto se asigna al marco de agregación de MongoDB en general.