Mongodb和聚合框架。对数组的元素求和以及文档的元素

时间:2014-02-25 15:18:55

标签: mongodb mongodb-query aggregation-framework

有很多文件:

{
        "_id"   : ObjectId("506ddd1900a47d802702a904"),
        "subid" : "s1",
        "total" : "300",
        "details" :[{
                      name:"d1", value: "100"
                    },
                    {
                      name:"d2", value: "200"
                    }]
}
{
        "_id"   : ObjectId("306fff1900a47d802702567"),
        "subid" : "s1",
        "total" : "700",
        "details" : [{
                      name:"d1", value: "300"
                    },
                    {
                      name:"d8", value: "400"
                    }]
 }

'详细信息'数组中的元素可能会有所不同。

问题是:如何通过聚合框架和java获得这样的结果?

{
        "_id"     : "s1",
        "total"   : "1000",
        "details" : [{
                      name:"d1", value: "400"
                    },
                    {
                      name:"d2", value: "200"
                    },
                    {
                      name:"d8", value: "400"
                    }]
 }

或许我应该在这里使用自定义map-reduce功能?

1 个答案:

答案 0 :(得分:2)

这是可以通过聚合实现的,虽然有点迟钝,但让我们通过它:

db.collection.aggregate([

    // First Group to get the *master* total for the documents
    {"$group": {
        "_id": "$subid",
         "total": { "$sum": "$total" },
         details: { "$push": "$details" } 
     }},

     // Unwind the details
     {"$unwind": "$details"},

     // Unwind the details "again" since you *pushed* and array onto an array
     {"$unwind":"$details"},

     // Now sum up the values by each name (keeping levels)
     {"$group": {
         "_id:" {
              "_id": "$_id",
              "total": "$total",
              "name":  "$details.name"
          },
          "value": {"$sum": "$details.value"}
      }},

     // Sort the names (because you expect that!)
     {"$sort": { "_id.name": 1}},

     // Do some initial re-shaping for convenience
     {"$project": {
         "_id": "$_id._id",
         "total": "$_id.total",
         "details": { "name": "$_id.name", "value": "$value" }
     }},

     // Now push everything back into an array form
     {"$group": {
         "_id": {
              "_id": "$_id",
              "total": "$total"
         },
         "details": {"$push": "$details"}
     }},

     // And finally project nicely
     {"$project": {
         "_id": "$_id._id",
         "total": "$_id.total",
         "details": 1 
     }}
])

因此,如果您之前尝试,您可能错过了执行初始组的概念,以便在total上获得顶级总和文档中的字段。

不可否认,棘手的位是“让你的头脑”整个双重展开接下来的事情。由于在第一个组中我们将数组推送到另一个数组,我们现在最终得到了这个新的嵌套需要 展开 两次的结构,以便达到“非规范化”形式。

完成后,您只需$groupname字段:

  

equiv(GROUP BY _id,total,“details.name”)

或多或少那样有一些明智的重塑。然后我要求按name键排序(因为你以那种方式打印),最后我们$project进入你想要的实际形式。

所以宾果,我们有你的结果。感谢您提供了一个很酷的问题来展示双重展开的使用。