Kể từ MongoDB 3.4, điều này có thể đạt được đơn giản hơn một chút bằng cách sử dụng nhiều đường ống tổng hợp với $facet
.
được lấy từ tài liệu :
Vì vậy, đối với trường hợp sử dụng của bạn, điều này sẽ đạt được bằng cách sau:
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)
Cái nào tạo ra những thứ sau:
[
{
"race": {
"Halfling": 3,
"Naga": 2
},
"class": {
"Hunter": 3,
"Rogue": 1,
"Fighter": 1,
}
}
]
Nếu bạn hài lòng với định dạng đầu ra được cung cấp @Asya, thì bạn có thể xóa $project
và $addFields
và chỉ cần để lại $sortByCount
một phần trong mỗi đường ống phụ.
Với những tính năng mới này, việc mở rộng tập hợp dễ dàng hơn rất nhiều với số lượng bổ sung, Chỉ cần thêm một đường dẫn tổng hợp khác trong $facet
.Để đếm các nhóm phụ thậm chí còn dễ dàng hơn một chút, nhưng đó sẽ là một câu hỏi riêng.