Dùng $unwind + $group đếm điều kiện trên mảng lồng
Kết hợp $unwind (flatten mảng) với $group và $cond để đếm số phần tử thỏa mãn điều kiện (ví dụ: Name không null).
1. Dữ liệu: Details.Information mảng object có Name (có null)
> db.demo478.insertOne({
Details: {
_id: 1,
Information: [
{ Name: "Chris", Age: 21 },
{ Name: "David", Age: 23 },
{ Name: null, Age: 22 },
{ Name: null, Age: 24 }
]
}
});
> db.demo478.insertOne({
Details: {
_id: 2,
Information: [
{ Name: "Bob", Age: 21 },
{ Name: null, Age: 20 }
]
}
});
2. Pipeline: Đếm số Name != null theo Age
> db.demo478.aggregate([
{ $unwind: "$Details.Information" },
{
$group: {
_id: "$Details.Information.Age",
count: {
$sum: {
$cond: [
{ $gt: ["$Details.Information.Name", null] },
1,
0
]
}
}
}
},
{ $sort: { count: -1 } }
]);
3. Kết quả
{ "_id": 21, "count": 2 }
{ "_id": 23, "count": 1 }
{ "_id": 24, "count": 0 }
{ "_id": 20, "count": 0 }
{ "_id": 22, "count": 0 }
Giải thích
- $unwind: Tách mảng Information → 6 document (4 + 2)
- $group by Age: Gom theo Age
- $cond: Nếu Name > null (tức không null) → cộng 1, else cộng 0
- $sort: Sắp xếp count giảm dần
Cách viết gọn hơn (MongoDB 3.6+)
// Dùng $sum với boolean (true=1, false=0)
{ $group: {
_id: "$Details.Information.Age",
count: { $sum: { $cond: [{ $ne: ["$Details.Information.Name", null] }, 1, 0] } }
}}
// Hoặc dùng $filter + $size (trong $project, không cần $unwind)
db.demo478.aggregate([
{ $project: {
AgeCounts: {
$map: {
input: "$Details.Information",
as: "item",
in: { Age: "$$item.Age", hasName: { $ne: ["$$item.Name", null] } }
}
}
}},
{ $unwind: "$AgeCounts" },
{ $group: { _id: "$AgeCounts.Age", count: { $sum: { $cond: ["$AgeCounts.hasName", 1, 0] } } } },
{ $sort: { count: -1 } }
]);
So sánh hiệu suất
| Cách | Ưu điểm | Nhược điểm |
|---|---|---|
| $unwind + $group | Đơn giản, hiểu dễ | Tạo nhiều doc trung gian, chậm mảng lớn |
| $filter + $size (no unwind) | Giữ cấu trúc, nhanh hơn mảng nhỏ | Phức tạp hơn, giới hạn 16MB doc |
| $reduce | Xử lý trong 1 doc, không unwind | Khó đọc, debug |
Mẹo: Đếm distinct Name theo Age
db.demo478.aggregate([
{ $unwind: "$Details.Information" },
{ $match: { "Details.Information.Name": { $ne: null } } },
{ $group: { _id: "$Details.Information.Age", uniqueNames: { $addToSet: "$Details.Information.Name" } } },
{ $project: { _id: 1, count: { $size: "$uniqueNames" } } },
{ $sort: { count: -1 } }
]);