【问题标题】:Calculate the count of nested objects with C#用 C# 计算嵌套对象的数量
【发布时间】:2018-05-08 23:17:08
【问题描述】:

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

{
    "_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 中编写该查询?

【问题讨论】:

    标签: c# mongodb linq aggregation-framework


    【解决方案1】:

    在"Tags" 中的"Uid" 和"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 987654348@ 和 $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# 驱动程序映射应用的 null 或 0:

    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 }
    

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

    我建议您熟悉核心文档中的Aggregation Operators,当然还有表达式的"LINQ Cheat Sheet" 及其使用C# 驱动程序代码库的使用映射。

    另请参阅 C# 驱动程序参考中的一般 LINQ Reference,了解其如何映射到一般 MongoDB 聚合框架的其他示例。

    【讨论】:

    • 您好,非常感谢您的回答。真的很好。但是在这种情况下,对于每个唯一的EndpointId,所有唯一Tags.Uid 的总和定义了该端点有多少标签。 sum of (sum of every unique Sensors.Type for every unique Tags.Uid) 定义了这个端点有多少个传感器。我认为你上面写的查询没有给出正确的答案。
    • @M.Taherian 为上述条件添加。请确保在未来的问题中,您实际上在问题中包含了您提出的此类详细信息。问题得到解答。如果您有其他问题或新条件,请改用Ask a New Question。你通常会发现人们不准备改变你最初要求的答案,所以这是一次性的。
    • 我只想说谢谢你写了这个惊人的答案,这对我学习和理解 mongodb 中的聚合和 LINQ 有很大帮助。
    猜你喜欢
    • 2016-09-27
    • 1970-01-01
    • 2017-11-24
    • 2017-06-18
    • 1970-01-01
    • 2020-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多