聚合来自两个数组的$ sum值

时间:2015-03-03 08:50:43

标签: mongodb mongodb-query aggregation-framework

我有这个集合

{
    "_id" : ObjectId("54f46f18c36dcc206d0cec38"),
    "project" : 23123,
    "title" : "Change of windows",
    "description": "Change to better windows on building A"
    "costs":[
      {
        category: 'Produktionskostnad',
        value: 3000
      },
      {
        category: 'Projekteringskostnad',
        value: 2000
      },
      {
        category: 'Overhead',
        value: 1000
      }
    ],
    "energySaving" : [ 
        {
            "energy" : "electricity",
            "type" : "lighting",
            "value" : 24324
        }, 
        {
            "energy" : "electricity",
            "type" : "equipment",
            "value" : 24324
        }, 
        {
            "energy" : "electricity",
            "type" : "fans",
            "value" : 24324
        }, 
        {
            "energy" : "electricity",
            "type" : "distribution",
            "value" : 24324
        }, 
        {
            "energy" : "electricity",
            "type" : "chiller",
            "value" : 24324
        }, 
        {
            "energy" : "electricity",
            "type" : "other",
            "value" : 24324
        }
    ]
}

我需要一个汇总来计算总成本和总节能量。

要获得保存,我有这个查询:

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

但是如何计算同一查询中的总费用?我不能在同一个查询中添加$ unwind cost。我可以以某种方式“重置”$ group并再次查询查询吗?

1 个答案:

答案 0 :(得分:5)

TLDR;

在现代MongoDB版本中,我们只执行一个$group,因为我们可以将数组项直接传递给$sum,并使用“数组求和”和“累加器”连续的“双”表示法: / p>

db.collection.aggregate([
    { "$group": {
        "_id": {
            "title": "$title",
            "description": "$description"
        },
        "totalCosts": { "$sum": { "$sum": "$costs.value" } },
        "totalEnergySaving": { "$sum": { "$sum": "$energySaving.value" } }
     }}
 ])

2015年原创答案

这需要一些杂耍才能正确完成,但描述它的最佳方法是“首先处理每个文档的分组”然后“稍后对总计进行分组”:

db.collection.aggregate([
    // Do cost per document
    { "$unwind": "$costs" },
    { "$group": {
        "_id": "$_id",
        "title": { "$first": "$title" },
        "description": { "$first": "$description" },
        "totalCosts": { "$sum": "$costs.value" },
        "energySaving": { "$first": "$energySaving" }
    }},

    // Do energy saving per document
    { "$unwind": "$energySaving" },
    { "$group": {
        "_id": "$_id",
        "title": { "$first": "$title" },
        "description": { "$first": "$description" },
        "totalCosts": { "$first": "$totalCosts" },
        "totalEnergySaving": { "$sum": "$energySaving.value" }
    }},

    // Now sum the real grouping
    { "$group": {
        "_id": {
            "title": "$title",
            "description": "$description"
        },
        "totalCosts": { "$sum": "$totalCosts" },
        "totalEnergySaving": { "$sum": "$totalEnergySaving" }
    }}
])

通过处理数组值每个文档的单个值,并通过展开和分组“一次一个数组”来避免每个数组成员的项目复制,您可以形成一个基础来实现您实际的单一分组想。

因此,当您$unwind数组时,您将获得文档的多个副本,每个数组成员现在在每个文档副本中表示为单数值。你不想在这里做的是$unwind另一个阵列,而你已经有一个没有伤口,因为根据阵列有多少成员,它会以相同的方式创建文档的“更多副本”。 / p>

此时使用$group返回文档_id值可确保我们仅处理最初“未受伤”的文档的原始部分。像$sum这样的普通分组操作符仍然适用,但是$first可以用来在“数组之外”只提取那些复制字段值中的“只有一个”并且几乎​​将文档返回到它的“原始形式” “对于您要保留的字段,以及您有意从数组内容聚合的任何内容。

对你想要的每个数组重复一次,然后转到另一个$group语句,这次使用你之前创建的新的奇异求和值,一次只加一个文档。

这是在任何级别的分组中添加多个数组项的过程。当然,如果唯一的分组是在文档级别进行的,那么你可以在对每个数组进行分组后放弃,或者确实接受在客户端代码中做得更好。