使用C#计算嵌套对象的数量

我正在使用ASP.Net core 2.0 web api开发一个软件。 我需要计算我的集合中某些字段的数量。 我在MongoDB的集合中有数据,如下所示。 我需要找到我的collections中有多少个标签和多少个传感器。 特定端点具有多个标签,每个标签具有多个传感器。

{ "_id" : ObjectId("5aef51dfaf42ea1b70d0c4db"), "EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83", "DateTime" : ISODate("2018-05-06T19:05:02.666Z"), "Url" : "test", "Tags" : [ { "Uid" : "C1:3D:CA:D4:45:11", "Type" : 1, "DateTime" : ISODate("2018-05-06T19:05:02.666Z"), "Sensors" : [ { "Type" : 1, "Value" : NumberDecimal("-95") }, { "Type" : 2, "Value" : NumberDecimal("-59") }, { "Type" : 3, "Value" : NumberDecimal("11.029802536740132") }, { "Type" : 4, "Value" : NumberDecimal("27.25") }, { "Type" : 6, "Value" : NumberDecimal("2924") } ] }, { "Uid" : "C1:3D:CA:D4:45:11", "Type" : 1, "DateTime" : ISODate("2018-05-06T19:05:02.666Z"), "Sensors" : [ { "Type" : 1, "Value" : NumberDecimal("-95") }, { "Type" : 2, "Value" : NumberDecimal("-59") }, { "Type" : 3, "Value" : NumberDecimal("11.413037961112279") }, { "Type" : 4, "Value" : NumberDecimal("27.25") }, { "Type" : 6, "Value" : NumberDecimal("2924") } ] }, { "Uid" : "E5:FA:2A:35:AF:DD", "Type" : 1, "DateTime" : ISODate("2018-05-06T19:05:02.666Z"), "Sensors" : [ { "Type" : 1, "Value" : NumberDecimal("-97") }, { "Type" : 2, "Value" : NumberDecimal("-58") }, { "Type" : 3, "Value" : NumberDecimal("10.171658037099185") } ] } ] } /* 2 */ { "_id" : ObjectId("5aef51e0af42ea1b70d0c4dc"), "EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83", "Url" : "test", "Tags" : [ { "Uid" : "E2:02:00:18:DA:40", "Type" : 1, "DateTime" : ISODate("2018-05-06T19:05:04.574Z"), "Sensors" : [ { "Type" : 1, "Value" : NumberDecimal("-98") }, { "Type" : 2, "Value" : NumberDecimal("-65") }, { "Type" : 3, "Value" : NumberDecimal("7.845424441900629") }, { "Type" : 4, "Value" : NumberDecimal("0.0") }, { "Type" : 6, "Value" : NumberDecimal("3012") } ] }, { "Uid" : "12:3B:6A:1A:B7:F9", "Type" : 1, "DateTime" : ISODate("2018-05-06T19:05:04.574Z"), "Sensors" : [ { "Type" : 1, "Value" : NumberDecimal("-95") }, { "Type" : 2, "Value" : NumberDecimal("-59") }, { "Type" : 3, "Value" : NumberDecimal("12.939770381907275") } ] } ] } 

我想计算与特定EndpointId相关的标签和传感器的数量。 如何在mongoDB中编写该查询?

计算"Tags"中每个"Uid" "Tags""Sensors" "Type""EndpointId"内的“唯一”出现的查询将是:

 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"值实际上发生在集合中的多个文档中,然后$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 ,那么您只需使用该字段作为分组键,而不是使用C#驱动程序映射应用的null0

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

这当然是您给我们的两个文档样本的总和:

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

所以这些都是非常简单的结果,一旦习惯了语法,就可以执行简单的管道。

我建议您熟悉核心文档中的聚合操作符 ,当然还有表达式的“LINQ Cheat Sheet”以及它们与C#驱动程序代码库的使用映射。

另请参阅C#驱动程序参考中的常规LINQ参考 ,以获取有关如何将此映射到MongoDB的聚合框架的其他示例。