Mongodb 对出现的关键点及其值进行计数和分组
我有一个MongoDB集合,看起来像这样:Mongodb 对出现的关键点及其值进行计数和分组,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,我有一个MongoDB集合,看起来像这样: [{ "installer": "anthony", "tester": "bob" }, { "installer": "chris", "tester": "anthony" }, { "installer": "bob", "tester": "dave" }, { "installer": "anthony",
[{
"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
}
]
这是我到目前为止完成的查询,问题是它只返回名称
和安装程序
计数,而不返回测试仪
计数。我可以运行此查询两次(一次用于installer
,另一次用于tester
),但我想找到一种方法,可以同时返回两个计数
db.data.aggregate([
{
"$group": {
"_id": "$installer",
"installer": { "$sum": 1 }
},
"$project": {
"name": "$_id",
"installer": 1,
"_id": 0
}
}
])
需要对我的查询进行哪些更改,以便我可以获得每个人的安装程序
和测试仪
计数?您基本上需要选择是将1
还是0
传递到管道中的累加器,以及作为“数组”的初始值用于两个字段,用于为每个人创建文档副本
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" }
}}
])
为了应对给定文档可能同时具有相同的“installer”和“tester”值的情况,我们实际上应该首先根据发出的“val”在“document”上进行聚合。在累加器内部使用将使本例成为“单个”文档,而不是“两个”,即每个数组条目对应一个文档
当然,另一种情况是通过对初始列表应用来简单地返回“集合”值,以避免在这种情况下重复:
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" }
为了说明结果
至于,它是一个“三元”或if..then..else
条件,其中参数为“first”if
,用于将条件计算为布尔值,then
是当true
时返回的值,else
是当条件为false
时返回的值
它可以交替写为:
"$cond": {
"if": { "$eq": ["$installer","$val"] },
"then": 1,
"else": 0
}
但是对于简单表达式来说,最初的“array”语法要简短一些。大多数人仍然会认识到“三元”是什么,但如果您认为它使代码更清晰,那么您可以使用“命名键”形式
当然,结果是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": {
"tester": 1,
"installer": 1,
"val": { "$setUnion": [["$installer","$tester"]] }
}}
如果您的MongoDB实际上仍然比MongoDB 3.2旧,后者允许使用“数组”符号,那么您可以使用MongoDB 2.6及更高版本:
{ "$project": {
"tester": 1,
"installer": 1,
"val": {
"$setUnion": [
{ "$map": {
"input": ["A","B"],
"as": "a",
"in": {
"$cond": [{ "$eq": ["$$a", "A"] }, "$installer", "$tester"]
}
}
]
}
}}
再次使用以交替选择要显示为数组元素的值
此外,您确实应该避免在语句末尾添加a之类的操作。您当然可以这样做,但这确实意味着前一管道阶段的所有结果都将“再次运行”,以便进行其他更改。对于像将“\u id”
更改为“name”
这样琐碎的事情,通常更好的做法是简单地接受“分组键”被称为\u id
,并保持不变
因此,实际上是
\u id
是常用术语的“唯一标识符”。这应该会对您有所帮助:感谢您的回复和详细解释。查询似乎是实际数字的两倍,因此不是得到“安装程序”:2
和“测试仪”:1
对于anthony
,它分别返回4
和2
。当安装程序
和测试员
是同一个人时会发生这种情况,例如安装程序“:“bob”,“tester”:bob
@dat3450当然正确,因为这种情况会产生“两个”由于$unwind
结果而导致的文档。解决方案是添加一个中介$group
阶段,使这样的结果成为一个文档而不是两个文档。请参阅添加的修改和解释。谢谢,我还提出了另一个解决方案:在$addToFields.val
中包含$cond
,它将检查$installer
和$tester
是否相同,并仅在相同的情况下输入一次。