如何对MongoDB子文档中的每个字段求和?

aai*_*ntw 6 mongodb aggregation-framework

我在MongoDB中使用db.collection.aggregate时出现问题.

我有一个数据结构,如:

_id:...

Segment:{
  "S1":1,
  "S2":5,
  ...
  "Sn":10
}
Run Code Online (Sandbox Code Playgroud)

它意味着以下内容Segment:我可能有几个具有数值的子属性.我想把它们总结为1 + 5 + .. + 10

问题是:我不确定子属性名称,因为每个文档的段号都不同.所以我无法列出每个细分名称.我只是想使用类似for循环的东西来将所有值加在一起.

我试过像这样的查询:

db.collection.aggregate([

  {$group:{
    _id:"$Account",

    total:{$sum:"$Segment.$"}
])
Run Code Online (Sandbox Code Playgroud)

但它不起作用.

Der*_*ick 15

你已经犯了经典错误,拥有任意字段名称.MongoDB是"无架构的",但这并不意味着您不需要考虑您的架构.键名应该是描述性的,在你的情况下,fe"S2"并不是真正的意思.为了执行大多数类型的查询和操作,您需要重新设计架构以存储数据,如下所示:

_id:...
Segment:[
    { field: "S1", value: 1 },
    { field: "S2", value: 5 },
    { field: "Sn", value: 10 },
]
Run Code Online (Sandbox Code Playgroud)

然后,您可以运行以下查询:

db.collection.aggregate( [
    { $unwind: "$Segment" },
    { $group: {
        _id: '$_id', 
        sum: { $sum: '$Segment.value' } 
    } } 
] );
Run Code Online (Sandbox Code Playgroud)

然后会产生类似的结果(使用您问题中的唯一文档):

{
    "result" : [
        {
            "_id" : ObjectId("51e4772e13573be11ac2ca6f"),
            "sum" : 16
        }
    ],
    "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)


Xav*_*hot 9

首先Mongo 3.4,这可以通过应用内联操作来实现,从而避免昂贵的操作,例如$group

// { _id: "xx", segments: { s1: 1, s2: 3, s3: 18, s4: 20 } }
db.collection.aggregate([
  { $addFields: {
      total: { $sum: {
        $map: { input: { $objectToArray: "$segments" }, as: "kv", in: "$$kv.v" }
      }}
  }}
])
// { _id: "xx", total: 42, segments: { s1: 1, s2: 3, s3: 18, s4: 20 } }
Run Code Online (Sandbox Code Playgroud)

这个想法是将对象(包含要求和的数字)转换为数组。这就是 的角色$objectToArray,其中开始Mongo 3.4.4,转变{ s1: 1, s2: 3, ... }[ { k: "s1", v: 1 }, { k: "s2", v: 3 }, ... ]。这样,我们就不需要关心字段名称,因为我们可以通过它们的"v"字段访问值。

拥有数组而不是对象是能够对其元素求和的第一步。但是用 with 获得的元素$objectToArray是对象而不是简单的整数。我们可以通过映射($map操作)这些数组元素来提取它们"v"字段的值来传递它。这在我们创建这个类型的数组的情况下的结果:[1, 3, 18, 42]

最后,使用该$sum操作对数组中的元素求和是一个简单的问题。

  • 好主意。这对我有帮助。 (2认同)