Xer*_*oid 6 mongodb mongodb-query aggregation-framework
是否有可能将聚合函数的结果作为key:count?
例:
我有以下聚合查询:
db.users.aggregate([
{
$group: {
_id: "$role",
count: {
$sum: 1
}
}
}
])
Run Code Online (Sandbox Code Playgroud)
结果如下:
{ "_id" : "moderator", "count" : 469 }
{ "_id" : "superadmin", "count" : 1 }
{ "_id" : "user", "count" : 2238 }
{ "_id" : "admin", "count" : 11 }
Run Code Online (Sandbox Code Playgroud)
所以这一切都很好,但有没有一种方法(可能使用$project)使结果看起来像这样(即使用role键作为键和count值):
{ "moderator": 469 }
{ "superadmin": 1 }
{ "user": 2238 }
{ "admin": 11 }
Run Code Online (Sandbox Code Playgroud)
我可以通过使用JS对结果进行后处理来做到这一点,但我的目标是直接通过聚合函数来完成.
使用 MongoDb 3.6 及更新版本,您可以利用$arrayToObject运算符和$replaceRoot管道的使用来获得所需的结果。您需要运行以下聚合管道:
db.users.aggregate([
{
"$group": {
"_id": { "$toLower": "$role" },
"count": { "$sum": 1 }
}
},
{
"$group": {
"_id": null,
"counts": {
"$push": {
"k": "$_id",
"v": "$count"
}
}
}
},
{
"$replaceRoot": {
"newRoot": { "$arrayToObject": "$counts" }
}
}
])
Run Code Online (Sandbox Code Playgroud)
对于旧版本,管道步骤中的$cond运算符$group可以有效地用于根据角色字段值评估计数。您可以按如下方式构建整体聚合管道,以生成所需格式的结果:
db.users.aggregate([
{
"$group": {
"_id": null,
"moderator_count": {
"$sum": {
"$cond": [ { "$eq": [ "$role", "moderator" ] }, 1, 0 ]
}
},
"superadmin_count": {
"$sum": {
"$cond": [ { "$eq": [ "$role", "superadmin" ] }, 1, 0 ]
}
},
"user_count": {
"$sum": {
"$cond": [ { "$eq": [ "$role", "user" ] }, 1, 0 ]
}
},
"admin_count": {
"$sum": {
"$cond": [ { "$eq": [ "$role", "admin" ] }, 1, 0 ]
}
}
}
},
{
"$project": {
"_id": 0,
"moderator": "$moderator_count",
"superadmin": "$superadmin_count",
"user": "$user_count",
"admin": "$admin_count"
}
}
])
Run Code Online (Sandbox Code Playgroud)
从注释跟踪中,如果您事先不知道角色并想动态创建管道数组,请distinct在角色字段上运行命令。这将为您提供一个包含不同角色列表的对象:
var result = db.runCommand ( { distinct: "users", key: "role" } )
var roles = result.values;
printjson(roles); // this will print ["moderator", "superadmin", "user", "admin"]
Run Code Online (Sandbox Code Playgroud)
现在给出上面的列表,您可以通过创建一个对象来组装您的管道,该对象将使用 JavaScript 的reduce()方法设置其属性。下面证明了这一点:
var groupObj = { "_id": null },
projectObj = { "_id": 0 }
var groupPipeline = roles.reduce(function(obj, role) { // set the group pipeline object
obj[role + "_count"] = {
"$sum": {
"$cond": [ { "$eq": [ "$role", role ] }, 1, 0 ]
}
};
return obj;
}, groupObj );
var projectPipeline = roles.reduce(function(obj, role) { // set the project pipeline object
obj[role] = "$" + role + "_count";
return obj;
}, projectObj );
Run Code Online (Sandbox Code Playgroud)
在最终聚合管道中使用这两个文档作为:
db.users.aggregate([groupPipeline, projectPipeline]);
Run Code Online (Sandbox Code Playgroud)
检查下面的演示。
db.users.aggregate([
{
"$group": {
"_id": { "$toLower": "$role" },
"count": { "$sum": 1 }
}
},
{
"$group": {
"_id": null,
"counts": {
"$push": {
"k": "$_id",
"v": "$count"
}
}
}
},
{
"$replaceRoot": {
"newRoot": { "$arrayToObject": "$counts" }
}
}
])
Run Code Online (Sandbox Code Playgroud)
db.users.aggregate([
{
"$group": {
"_id": null,
"moderator_count": {
"$sum": {
"$cond": [ { "$eq": [ "$role", "moderator" ] }, 1, 0 ]
}
},
"superadmin_count": {
"$sum": {
"$cond": [ { "$eq": [ "$role", "superadmin" ] }, 1, 0 ]
}
},
"user_count": {
"$sum": {
"$cond": [ { "$eq": [ "$role", "user" ] }, 1, 0 ]
}
},
"admin_count": {
"$sum": {
"$cond": [ { "$eq": [ "$role", "admin" ] }, 1, 0 ]
}
}
}
},
{
"$project": {
"_id": 0,
"moderator": "$moderator_count",
"superadmin": "$superadmin_count",
"user": "$user_count",
"admin": "$admin_count"
}
}
])
Run Code Online (Sandbox Code Playgroud)