计算mongo聚合中的记录类型(pymongo)

时间:2014-08-02 17:52:30

标签: mongodb pymongo mongodb-query aggregation-framework

我正在使用pymongo进行以下聚合(users先前被定义为我想要查询的用户列表):

    pipeline = [
        { '$match': {'user': {'$in': users} },
        { '$group': { '_id': "$user", 'badges': {'$push': '$badge'} } },
    ]

这给了我以下结果:

{u'ok': 1.0,
 u'result': [{u'_id': u'user22',
              u'badges': [u'gold', u'silver', u'silver']},
             {u'_id': u'user2',
              u'badges': [u'gold', u'gold']},
             {u'_id': u'user15',
              u'badges': [u'gold', u'bronze', u'bronze']},
             {u'_id': u'user11',
              u'badges': [u'gold']},
             {u'_id': u'user3',
              u'badges': [u'silver', u'bronze']},
             {u'_id': u'user18',
              u'badges': [u'bronze']}
            ]
}

这没关系,但我真正想要得到的是每枚奖牌类型的数量(类型=金/银/铜牌)。我可以在Python中进行后期处理时轻松完成这项工作,但我觉得我应该能够在同一个管道中完成这项工作,并且我想学习“如何更好地使用mongo”:)

所以要清楚,我真正想要的是这个(我手动生成了这个理想的输出,因此可能存在上述数据的不一致或语法错误,但我认为它得到了重点):

{u'ok': 1.0,
 u'result': [{u'_id': u'user22',
              u'badges': {u'gold': 1, u'silver': 2}},
             {u'_id': u'user2',
              u'badges': {u'gold': 2}},
             {u'_id': u'user15',
              u'badges': {u'gold': 1, u'bronze': 2}},
             {u'_id': u'user11',
              u'badges': {u'gold': 1}},
             {u'_id': u'user3',
              u'badges': {u'silver': 1, u'bronze': 1}},
             {u'_id': u'user18',
              u'badges': {u'bronze': 1}}
            ]
}

我的数据结构要求并不严格。我也很乐意使用金/银/铜作为键并避免使用嵌套的字典:

{u'_id': u'user22',
 u'gold': 1, u'silver': 2},
{u'_id': u'user2',
 u'gold': 2},
...

我尝试用$sum运算符做了很多事情,但没有运气。当我尝试动态生成字段名称时,我得到:

failed: exception: the group aggregate field name '$badge' cannot be an operator name

有什么想法吗?提前谢谢!

(另外,半相关...我对map-reduce知之甚少。也许这是一个候选人。我开始使用聚合,他们到目前为止工作到现在为止。我应该了解map-reduce)

1 个答案:

答案 0 :(得分:1)

除了将徽章推送到数组之外,你可以做的更多是在徽章类型上有条件地$sum。这通常是通过测试$eq运算符中的$cond条件来完成的,以确定对“总和”的贡献量:

collection.aggregate([
    { "$match": { "user": { "$in": users } } },
    { "$group": {
        "_id": "$user",
        "gold": { 
            "$sum": {
                "$cond": [
                    { "$eq": [ "$badge", "gold" ] },
                    1,
                    0
                ]
            }
        },
        "silver": { 
            "$sum": {
                "$cond": [
                     { "$eq": [ "$badge", "silver" ] },
                     1,
                     0
                ]
            }
        },
        "bronze": { 
            "$sum": {
                "$cond": [
                     { "$eq": [ "$badge", "bronze" ] },
                     1,
                     0
                ]
            }
        }
    }}
])

这将正确地对每种类型求和,当然每个用户都会有“金/银/铜”的计数,无论它是否大于0。你不能做的是在聚合框架中“动态”创建字段。

如果你真的需要“动态”字段,那么你唯一的选择是mapReduce,但当然这不会像聚合框架那样有效。条件总和确实给你最好的选择。