Mongo聚合嵌套数组

时间:2014-03-15 12:54:08

标签: mongodb mongodb-query aggregation-framework

我有一个具有以下结构的mongo集合

{
    "userId" : ObjectId("XXX"),
    "itemId" : ObjectId("YYY"),
    "resourceId" : 1,
    "_id" : ObjectId("528455229486ca3606004ec9"),
    "parameter" : [
        {
            "name" : "name1",
            "value" : 150,
            "_id" : ObjectId("528455359486ca3606004eed")
        },
        {
            "name" : "name2",
            "value" : 0,
            "_id" : ObjectId("528455359486ca3606004eec")
        },
        {
            "name" : "name3",
            "value" : 2,
            "_id" : ObjectId("528455359486ca3606004eeb")
        }
    ]
}

可能有多个文档具有相同的'useId'且具有不同的'itemId',但参数在所有文档中都具有相同的键/值对。

我想要完成的是为每个唯一的“userId”返回聚合参数“name1”,“name2”和“name3”而忽略'itemId'。所以每个用户的最终结果都是如此:

{
  "userId" : ObjectId("use1ID"),
  "name1" : (aggregatedValue),
  "name2" : (aggregatedValue),
  "name3" : (aggregatedVAlue) 
},
{
  "userId" : ObjectId("use2ID"),
  "name1" : (aggregatedValue),
  "name2" : (aggregatedValue),
  "name3" : (aggregatedVAlue) 
}

是否可以使用mongoDB的聚合方法完成此操作?你能帮我建一个正确的查询来完成吗?

2 个答案:

答案 0 :(得分:12)

最简单的形式是通过“参数”“名称”来保持键入:

db.collection.aggregate(
   // Unwind the array
   { "$unwind": "$parameter"},

   // Group on the "_id" and "name" and $sum "value"
   { "$group": {
       "_id": { 
           "userId": "$userId",
           "name": "$parameter.name"
       }, 
       "value": { "$sum": "$parameter.value" } 
   }},

   // Put things into an array for "nice" processing
   { "$group": {
       "_id": "$_id.userId",
       "values": { "$push": { 
           "name": "$_id.name",
           "value": "$value"
       }}
   }}
)

如果确实需要将名称的“值”作为字段值,则可以执行以下操作。但是,由于您正在“投影”字段/属性,因此您必须在代码中指定所有。你不能再“动态”了,而且每个人都在编码/生成

db.collection.aggregate([
    // Unwind the array
    { "$unwind": "$parameter"},

   // Group on the "_id" and "name" and $sum "value"
    { "$group": {
        "_id": {
             "userId": "$userId",
             "name": "$parameter.name"
        },
        "value": { "$sum": "$parameter.value"}
    }},

    // Project out discrete "field" names with $cond
    { "$project": {
        "name1": { "$cond": [
            { "$eq": [ "$_id.name", "name1" ] },
            "$value",
            0
        ]},
        "name2": { "$cond": [
            { "$eq": [ "$_id.name", "name2" ] },
            "$value",
            0
        ]},
        "name3": { "$cond": [
            { "$eq": [ "$_id.name", "name3" ] },
            "$value",
            0
        ]},
    }},

    // The $cond put "0" values in there. So clean up with $group and $sum
    { "$group": { 
        _id: "$_id.userId",
        "name1": { "$sum": "$name1" },
        "name2": { "$sum": "$name2" },
        "name3": { "$sum": "$name3" }
    }}

])

因此,虽然额外的步骤可以为您提供所需的结果(以及最终项目将_id更改为userId),但我认为短版本足够可行,除非您真的确实需要它。考虑那里的输出:

{
    "_id" : ObjectId("53245016ea402b31d77b0372"),
    "values" : [
        {
            "name" : "name3",
            "value" : 2
        },
        {
            "name" : "name2",
            "value" : 0
        },
        {
            "name" : "name1",
            "value" : 150
        }
    ]
}

这就是我个人用的东西。但是你的选择。

答案 1 :(得分:1)

不确定我是否收到您的问题,但如果名称字段只能包含"name1", "name2", "name3",或者至少您只对此值感兴趣,则可能的查询之一可能就是:

db.aggTest.aggregate(
    {$unwind:"$parameter"}, 
    {$project: {"userId":1, "parameter.name":1, 
       "name1" : {"$cond": [{$eq : ["$parameter.name", "name1"]}, "$parameter.value", 0]},
       "name2" : {"$cond": [{$eq : ["$parameter.name", "name2"]}, "$parameter.value", 0]}, 
       "name3" : {"$cond": [{$eq : ["$parameter.name", "name3"]}, "$parameter.value", 0]}}}, 
    {$group : {_id : {userId:"$userId"}, 
       name1 : {$sum:"$name1"}, 
       name2 : {$sum:"$name2"}, 
       name3 : {$sum:"$name3"}}})

首先展开参数数组,然后将name1,name2和name3值分隔为不同的列。对此有一个简单的条件声明。之后,我们可以通过新列轻松地进行聚合。

希望它有所帮助!