【问题标题】:How to sum every fields in a sub document of MongoDB?如何汇总 MongoDB 子文档中的每个字段?
【发布时间】:2013-07-13 17:50:59
【问题描述】:

我在 MongoDB 中使用 db.collection.aggregate 时遇到问题。

我有一个像这样的数据结构:

_id:...

Segment:{
  "S1":1,
  "S2":5,
  ...
  "Sn":10
}

Segment 中的含义如下:我可能有几个带有数值的子属性。我想把它们总结为 1 + 5 + .. + 10

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

我试过这样的查询:

db.collection.aggregate([

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

    total:{$sum:"$Segment.$"}
])

但它不起作用。

【问题讨论】:

  • 您需要将架构更改为类似于segment: 5, value: 400 并将它们放入数组中,或者使用 MapReduce。您不能对任意字段进行求和。

标签: mongodb aggregation-framework


【解决方案1】:

您犯了一个经典的错误,即使用任意字段名称。 MongoDB 是“无模式的”,但这并不意味着您不需要考虑您的模式。键名应该是描述性的,在你的情况下,f.e. “S2”实际上没有任何意义。为了执行大多数类型的查询和操作,您需要重新设计架构以存储数据,如下所示:

_id:...
Segment:[
    { field: "S1", value: 1 },
    { field: "S2", value: 5 },
    { field: "Sn", value: 10 },
]

然后您可以像这样运行查询:

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

然后结果是这样的(您问题中的唯一文件):

{
    "result" : [
        {
            "_id" : ObjectId("51e4772e13573be11ac2ca6f"),
            "sum" : 16
        }
    ],
    "ok" : 1
}

【讨论】:

  • 谢谢。我想你是对的。但问题是我已经加载了相当大的整个数据集。有什么方法可以就地转换吗?
  • 我不这么认为。您必须编写一个脚本来逐一执行此操作。如果脚本已经在生产环境中运行,请确保限制脚本。
  • 嗯,答案确实很有帮助,但 $unwind 可能会导致大型数据集出现一些严重的性能问题。所以我会先尝试考虑一个更好的解决方案。
  • 我觉得这个问题和stackoverflow.com/questions/56640054/…这个问题差不多
【解决方案2】:

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 } }

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

拥有一个数组而不是一个对象是能够对其元素求和的第一步。但是使用$objectToArray 获得的元素是对象而不是简单的整数。我们可以通过映射($map 操作)这些数组元素来提取它们的"v" 字段的值。在我们的例子中,这会导致创建这种数组:[1, 3, 18, 42]

最后,使用$sum 操作对这个数组中的元素求和是一件简单的事情。

【讨论】:

  • 好主意。这对我有帮助。
【解决方案3】:

段:{s1: 10, s2: 4, s3: 12}

{$set: {"new_array":{$objectToArray: "$Segment"}}}, //makes field names all "k" or "v"
{$project: {_id:0, total:{$sum: "$new_array.v"}}} 

“总数”将为 26。

$set 替换了较新版本的 mongo 中的 $addFields。 (我使用的是 4.2。)

"new_array": [
                {
                    "k": "s1",
                    "v": 10
                },
                {
                    "k": "s2",
                    "v": 4
                },
                {
                    "k": "s3",
                    "v": 12
                }
            ]

您也可以使用正则表达式。例如。 /^s/i 表示以“s”开头的单词。

【讨论】:

    猜你喜欢
    • 2014-09-30
    • 2018-06-06
    • 2022-11-24
    • 1970-01-01
    • 2014-11-12
    • 1970-01-01
    • 2021-06-24
    • 2022-12-04
    • 2014-10-12
    相关资源
    最近更新 更多