MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Groupe MongoDB et somme avec id comme clé

Avec MongoDb 3.6 et versions ultérieures, vous pouvez tirer parti de l'utilisation de $arrayToObject opérateur et un $replaceRoot pipeline pour obtenir le résultat souhaité. Vous auriez besoin d'exécuter le pipeline agrégé suivant :

db.users.aggregate([
    { 
        "$group": {
            "_id": { "$toLower": "$role" },
            "count": { "$sum": 1 }
        }
    },
    { 
        "$group": {
            "_id": null,
            "counts": {
                "$push": {
                    "k": "$_id",
                    "v": "$count"
                }
            }
        }
    },
    { 
        "$replaceRoot": {
            "newRoot": { "$arrayToObject": "$counts" }
        } 
    }    
])

Pour les anciennes versions, le $cond opérateur dans le $group L'étape de pipeline peut être utilisée efficacement pour évaluer les nombres en fonction de la valeur du champ de rôle. Votre pipeline d'agrégation global peut être construit comme suit pour produire le résultat dans le format souhaité :

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"
        }
    }
])

À partir de la piste de commentaires, si vous ne connaissez pas les rôles à l'avance et que vous souhaitez créer le tableau de pipeline de manière dynamique, exécutez le distinct commande sur le champ de rôle. Cela vous donnera un objet qui contient une liste des rôles distincts :

var result = db.runCommand ( { distinct: "users", key: "role" } )
var roles = result.values;
printjson(roles); // this will print ["moderator", "superadmin", "user",  "admin"]

Maintenant, étant donné la liste ci-dessus, vous pouvez assembler votre pipeline en créant un objet dont les propriétés seront définies à l'aide de JavaScript reduce() méthode. Ce qui suit le démontre :

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 );

Utilisez ces deux documents dans votre pipeline d'agrégation final comme :

db.users.aggregate([groupPipeline, projectPipeline]);

Regardez la démo ci-dessous.

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>