是否可以将聚合函数的结果作为key:count
?
示例:
我有以下聚合查询:
db.users.aggregate([
{
$group: {
_id: "$role",
count: {
$sum: 1
}
}
}
])
所以结果如下:
{ "_id" : "moderator", "count" : 469 }
{ "_id" : "superadmin", "count" : 1 }
{ "_id" : "user", "count" : 2238 }
{ "_id" : "admin", "count" : 11 }
所以这一切都很好,但有没有一种方法(可能使用$project
)使结果看起来像这样(即以role
为关键点和{{1} }作为值):
count
我可以通过使用JS对结果进行后处理来实现这一点,但我的目标是直接通过聚合函数来实现。
答案 0 :(得分:3)
使用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" }
}
}
])
对于旧版本,$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"
}
}
])
在评论路径中,如果您事先不知道角色并想要动态创建管道阵列,请在角色字段上运行 distinct
命令。这将为您提供一个包含不同角色列表的对象:
var result = db.runCommand ( { distinct: "users", key: "role" } )
var roles = result.values;
printjson(roles); // this will print ["moderator", "superadmin", "user", "admin"]
现在给出上面的列表,您可以通过创建一个使用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 );
在最终聚合管道中使用这两个文档:
db.users.aggregate([groupPipeline, projectPipeline]);
查看下面的演示。
var roles = ["moderator", "superadmin", "user", "admin"],
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 );
var pipeline = [groupPipeline, projectPipeline]
pre.innerHTML = JSON.stringify(pipeline, null, 4);
<pre id="pre"></pre>
答案 1 :(得分:0)
您可以使用map-reduce:
db.users.mapReduce(
function map() { emit(this.role, 1); },
function reduce(key, values) {
var sum = 0;
for (var i = 0; i < values.length; i++) {
sum += i;
}
return sum;
},
{
out: { inline: 1 },
finalize: function (key, reducedValue) {
var obj = {};
obj[key] = reducedValue;
return obj;
}
}
)
并仅映射value