Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> MongoDB

MongoDB $unwind để đếm số lượng theo điều kiện

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

  1. $unwind: Tách mảng Information → 6 document (4 + 2)
  2. $group by Age: Gom theo Age
  3. $cond: Nếu Name > null (tức không null) → cộng 1, else cộng 0
  4. $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ểmNhượ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
$reduceXử lý trong 1 doc, không unwindKhó đọ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 } }
]);