来自“$ author”(MongoDB)的文档和子文档组的$ sum

时间:2015-01-10 22:40:10

标签: javascript node.js mongodb mongodb-query aggregation-framework

这是我的收藏:

{
        "_id" : 10926400,
        "votes": 131,
        "author": "Jesse",
        "comments" : [
                {
                        "id" : 1,
                        "votes": 31,
                        "author": "Mirek"
                },
                {
                        "id": 2,
                        "votes": 13,
                        "author": "Leszke"
                }
        ]
},
{
        "_id" : 10926401,
        "votes": 75,
        "author": "Mirek",
        "comments" : [
                {
                        "id" : 1,
                        "votes": 17,
                        "author": "Jesse"
                },
                {
                        "id": 2,
                        "votes": 29,
                        "author": "Mirek"
                }
        ]
}

我希望每个$sum的{​​{1}}和votes的值comments.votes

预期输出(author):

sort $votes: -1

2 个答案:

答案 0 :(得分:4)

不是立即可见但可能。您需要做的是将顶级文档与注释数组合并而不重复。这是首先将内容作为两个数组加入单个数组,然后$unwind对内容进行分组的方法:

db.collection.aggregate([
    { "$group": {
        "_id": "$_id",
        "author": { 
            "$addToSet": {
                "id": "$_id",
                "author": "$author",
                "votes": "$votes"
            }
        },
        "comments": { "$first": "$comments" }
    }},
    { "$project": {
        "combined": { "$setUnion": [ "$author", "$comments" ] }
    }},
    { "$unwind": "$combined" },
    { "$group": {
        "_id": "$combined.author",
        "votes": { "$sum": "$combined.votes" }
    }},
    { "$sort": { "votes": -1 } }
])

给出了输出:

{ "_id" : "Jesse", "votes" : 148 }
{ "_id" : "Mirek", "votes" : 135 }
{ "_id" : "Leszke", "votes" : 13 }

即使跳过第一个$group阶段并以不同的方式组合数组:

db.collection.aggregate([
    { "$project": {
        "combined": { 
            "$setUnion": [
                { "$map": {
                    "input": { "$literal": ["A"] },
                    "as": "el",
                    "in": { 
                        "author": "$author",
                        "votes": "$votes"
                    }
                }},
                "$comments"
            ] 
        }
    }},
    { "$unwind": "$combined" },
    { "$group": {
        "_id": "$combined.author",
        "votes": { "$sum": "$combined.votes" }
    }},
    { "$sort": { "votes": -1 } }
])

那些使用MongoDB 2.6中引入的$setUnion甚至$map等运算符。这使得它更简单,但它仍然可以在缺少这些运算符的早期版本中完成,遵循相同的原则:

db.collection.aggregate([
    { "$project": {
        "author": 1,
        "votes": 1,
        "comments": 1,
        "type": { "$const": ["A","B"] }
    }},
    { "$unwind": "$type" },
    { "$unwind": "$comments" },
    { "$group": { 
        "_id": {
          "$cond": [
              { "$eq": [ "$type", "A" ] },
              { 
                  "id": "$_id", 
                  "author": "$author",
                  "votes": "$votes"
              },
              "$comments"
          ]
        }
    }},
    { "$group": {
        "_id": "$_id.author",
        "votes": { "$sum": "$_id.votes" }
    }},
    { "$sort": { "votes": -1 } }
])

$const 未记录,但存在于聚合框架所在的所有MongoDB版本中(来自2.2)。 MongoDB 2.6引入了$literal,它基本上链接到相同的底层代码。这里有两种情况用于为数组提供模板元素,或者引入一个数组来展开,以便在两个动作之间提供“二元选择”。

答案 1 :(得分:2)

您可以按以下方式汇总结果:

  • Unwind条数组。
  • Group记录在一起首先计算投票总和 每位作者在评论中收到。同时保留原件 帖子很明智。
  • 原始帖子数组
  • Unwind
  • 现在project每位作者的总和。
  • Sort通过作者的姓名和投票。
  • 从每个组中选择第一条记录以消除重复记录。

代码:

db.collection.aggregate([
{$unwind:"$comments"},
{$group:{"_id":null,
         "comments":{$push:"$comments"},
         "post":{$addToSet:{"author":"$author",
                            "votes":"$votes"}}}},
{$unwind:"$comments"},
{$group:{"_id":"$comments.author",
         "votes":{$sum:"$comments.votes"},
         "post":{$first:"$post"}}},
{$unwind:"$post"},
{$project:{"_id":1,
           "votes":{$cond:[{$eq:["$_id","$post.author"]},
                           {$add:["$votes","$post.votes"]},
                           "$votes"]}}},
{$sort:{"_id":-1,"votes":-1}},
{$group:{"_id":"$_id","votes":{$first:"$votes"}}}
])

样本o / p:

{ "_id" : "Leszke", "votes" : 13 }
{ "_id" : "Jesse", "votes" : 148 }
{ "_id" : "Mirek", "votes" : 135 }