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

Joe*_*Joe 5 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
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

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

为了得到保存我有这个查询:

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

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

Nei*_*unn 5

TLDR;

在Modern MongoDB版本中,我们只需执行一个,$group因为我们可以将数组项直接传递$sum给"数组求和"和"累加器"的"双"表示法:

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

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" }
    }}
])
Run Code Online (Sandbox Code Playgroud)

通过处理数组值每个文档的单个值,并通过展开和分组"一次一个数组"来避免每个数组成员的项目复制,您形成了实际需要的单个分组的基础.

因此,当您$unwind使用数组时,您将获得文档的多个副本,每个数组成员现在在每个文档副本中表示为单数值.你不想在这里做的是$unwind另一个阵列,而你已经有一个没有伤口,因为这将以相同的方式创建该阵列具有多少个成员的文档的"更多副本".

此时使用$group返回文档_id值可确保我们仅处理最初"未缠绕"的文档的原始部分.正常的分组操作符$sum仍然适用,但$first可以用来在"数组之外"只提取那些复制字段值中的"只有一个",并且几乎将文档返回到它要保留的字段的"原始形式".你有意从数组内容聚合的任何东西.

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

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