От MongoDB 3.4 това може да се постигне малко по-лесно, като се използват множество конвейери за агрегиране с $facet
.
взето от документи :
Така че за вашия случай на употреба това ще бъде постигнато чрез следното:
const aggregatorOpts = [
{ $match: { character: 'broquaint' } }, // Match the character
{
// Seperate into 2 or more pipes that will count class and
// race seperatly
$facet: {
race: [
// Group by race and get the count:
// [
// {
// _id: 'Halfling',
// count: 3
// }
// {
// _id: 'Naga',
// count: 2
// }
// ]
// $sortByCount is the same as
// { $group: { _id: <expression>, count: { $sum: 1 } } },
// { $sort: { count: -1 } }
{ $sortByCount: '$race' },
// Now we want to transform the array in to 1 document,
// where the '_id' field is the key, and the 'count' is the value.
// To achieve this we will use $arrayToObject. According the the
// docs, we have to first rename the fields to 'k' for the key,
// and 'v' for the value. We use $project for this:
{
$project: {
_id: 0,
k: '$_id',
v: '$count',
},
},
],
// Same as above but for class instead
class: [
{ $sortByCount: '$class' },
{
$project: {
_id: 0,
k: '$_id',
v: '$count',
},
},
],
},
},
{
// Now apply the $arrayToObject for both class and race.
$addFields: {
// Will override the existing class and race arrays
// with their respective object representation instead.
class: { $arrayToObject: '$class' },
race: { $arrayToObject: '$race' },
},
},
];
db.races.aggregate(aggregatorOpts)
Което произвежда следното:
[
{
"race": {
"Halfling": 3,
"Naga": 2
},
"class": {
"Hunter": 3,
"Rogue": 1,
"Fighter": 1,
}
}
]
Ако сте доволни от изходното форматиране, предоставено @Asya, тогава можете да премахнете $project
и $addFields
етапи и просто оставете $sortByCount
част във всеки подтръбопровод.
С тези нови функции агрегирането е много по-лесно за разширяване с допълнителни преброявания. Просто добавете друг конвейер за агрегиране в $facet
.Даже е малко по-лесно да се преброят подгрупите, но това би било отделен въпрос.