我正在使用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)
答案 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,但当然这不会像聚合框架那样有效。条件总和确实给你最好的选择。