Com o MongoDb 3.6 e mais recente, você pode aproveitar o uso de
$arrayToObject
operador e um $replaceRoot
pipeline para obter o resultado desejado. Você precisaria executar o seguinte pipeline agregado:db.users.aggregate([
{
"$group": {
"_id": { "$toLower": "$role" },
"count": { "$sum": 1 }
}
},
{
"$group": {
"_id": null,
"counts": {
"$push": {
"k": "$_id",
"v": "$count"
}
}
}
},
{
"$replaceRoot": {
"newRoot": { "$arrayToObject": "$counts" }
}
}
])
Para versões mais antigas, o
$cond
operador no $group
etapa de pipeline pode ser usada efetivamente para avaliar as contagens com base no valor do campo de função. Seu pipeline de agregação geral pode ser construído da seguinte forma para produzir o resultado no formato desejado: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"
}
}
])
Na trilha de comentários, se você não souber os papéis de antemão e quiser criar a matriz de pipeline dinamicamente, execute o
distinct
comando no campo de função. Isso fornecerá um objeto que contém uma lista das funções distintas:var result = db.runCommand ( { distinct: "users", key: "role" } )
var roles = result.values;
printjson(roles); // this will print ["moderator", "superadmin", "user", "admin"]
Agora, dada a lista acima, você pode montar seu pipeline criando um objeto que terá suas propriedades definidas usando o
reduce()
do JavaScript método. O seguinte demonstra isso: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 );
Use estes dois documentos em seu pipeline de agregação final como:
db.users.aggregate([groupPipeline, projectPipeline]);
Confira a demonstração abaixo.
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>