我有一个看起来像这样的MongoDB集合:
[{
"installer": "anthony",
"tester": "bob"
}, {
"installer": "chris",
"tester": "anthony"
}, {
"installer": "bob",
"tester": "dave"
}, {
"installer": "anthony",
"tester": "chris"
}, {
"installer": "chris",
"tester": "dave"
}
]
我正在尝试使用aggregate
,因此我可以计算每个字段在每个字段中出现的次数,并检索以下结果:
[{
"name": "anthony",
"installer": 2,
"tester": 1
}, {
"name": "bob",
"installer": 1,
"tester": 1
}, {
"name": "chris",
"installer": 2,
"tester": 1
}, {
"name": "dave",
"installer": 0,
"tester": 2
}
]
这是我到目前为止完成的查询,问题是它只返回name
和installer
计数而没有tester
计数。我可以运行此查询两次(一个用于installer
,一个用于tester
)但我想找到一种方法如何一次返回两个计数。
db.data.aggregate([
{
"$group": {
"_id": "$installer",
"installer": { "$sum": 1 }
},
"$project": {
"name": "$_id",
"installer": 1,
"_id": 0
}
}
])
我的查询需要进行哪些更改才能获得每个人的installer
和tester
点数?
答案 0 :(得分:2)
您基本上希望$cond
选择是将1
或0
传递到$sum
管道中的$group
累加器,将初始值作为"阵列"使用$unwind
为两个字段创建每个人的文档副本。
db.data.aggregate([
{ "$addFields": {
"val": ["$installer","$tester"]
}},
{ "$unwind": "$val" },
{ "$group": {
"_id": { "_id": "$_id", "val": "$val" },
"installer": {
"$max": {
"$cond": [
{ "$eq": ["$installer","$val"] },
1,
0
]
}
},
"tester": {
"$max": {
"$cond": [
{ "$eq": ["$tester","$val"] },
1,
0
]
}
}
}},
{ "$group": {
"_id": "$_id.val",
"installer": { "$sum": "$installer" },
"tester": { "$sum": "$tester" }
}}
])
要对抗给定文档可以两者相同"安装程序"和"测试员"我们实际应该在"文件"上汇总的价值根据发出的" val"作为第一步。使用$cond
累加器中的$max
可以使此案例成为"单个"文档而不是"两个",每个数组条目一个。
另一种情况当然是简单地返回" set"通过对初始列表应用$setUnion
来避免在这样的实例中出现重复:
db.data.aggregate([
{ "$addFields": {
"val": { "$setUnion": [["$installer","$tester"]] }
}},
{ "$unwind": "$val" },
{ "$group": {
"_id": "$val",
"installer": {
"$sum": {
"$cond": [
{ "$eq": ["$installer","$val"] },
1,
0
]
}
},
"tester": {
"$sum": {
"$cond": [
{ "$eq": ["$tester","$val"] },
1,
0
]
}
}
}}
])
我在源文件中添加了一个文档:
{ "installer": "jack", "tester": "jack" }
为了说明结果。
对于$cond
,它是一个"三元"或if..then..else
条件,其中参数为" first" if
要求条件评估为布尔值,then
是在true
时返回的值,else
是条件为false
时要返回的值。
它可以交替写成:
"$cond": {
"if": { "$eq": ["$installer","$val"] },
"then": 1,
"else": 0
}
但原来的"阵列"为简单表达式编写语法要简单一些。大多数人仍然会认识到"三元"它是什么,但如果你认为它使代码更清晰,那么你可以使用"命名键"而不是形式。
结果当然是1
仅在文档中存在字段时返回,并给出正确的计数:
/* 1 */
{
"_id" : "jack",
"installer" : 1.0,
"tester" : 1.0
}
/* 2 */
{
"_id" : "dave",
"installer" : 0.0,
"tester" : 2.0
}
/* 3 */
{
"_id" : "bob",
"installer" : 1.0,
"tester" : 1.0
}
/* 4 */
{
"_id" : "chris",
"installer" : 2.0,
"tester" : 1.0
}
/* 5 */
{
"_id" : "anthony",
"installer" : 2.0,
"tester" : 1.0
}
添加初始"数组"如果您的MongoDB版本不支持$project
,则可以使用$addFields
替换文档。唯一的区别是"明确"包括以后需要的其他字段:
{ "$project": {
"tester": 1,
"installer": 1,
"val": { "$setUnion": [["$installer","$tester"]] }
}}
如果您的MongoDB实际上仍然比MongoDB 3.2更早,它允许使用#34;数组",那么您可以使用$map
代替MongoDB 2.6及更高版本:
{ "$project": {
"tester": 1,
"installer": 1,
"val": {
"$setUnion": [
{ "$map": {
"input": ["A","B"],
"as": "a",
"in": {
"$cond": [{ "$eq": ["$$a", "A"] }, "$installer", "$tester"]
}
}
]
}
}}
再次使用$cond
交替选择要呈现为数组元素的值。
另外,你真的应该避免在语句结尾添加$project
这样的事情。你当然可以这样做,但它确实意味着前一个流水线阶段的所有结果都是'#34;再次运行"为了进行额外的更改。对于将"_id"
更改为"name"
这样微不足道的事情,通常更好的做法是简单地接受"分组键"被称为_id
并留在那里。
作为$group
的结果,实际上 是"唯一标识符"其中_id
是常用命名法。