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

Lọc tài liệu MongoDB nơi tất cả phần tử mảng lồng thỏa mãn điều kiện

Để tìm tài liệu mà **tất cả** phần tử trong mảng lồng nhau (nested array) đều thỏa mãn một điều kiện (logic ALL), bạn không thể dùng `find()` đơn giản (vì `find` chỉ cần **một** phần tử khớp). Cần dùng **Aggregation Pipeline** để "bật mở" mảng ($unwind), kiểm tra từng phần tử, gom nhóm lại và so sánh số lượng.

1. Tạo dữ liệu: 2 user, mỗi user có mảng details (CountryName, Marks)

> db.demo418.insertOne({
    "details": [
        { "CountryName": "US", "Marks": 45 },
        { "CountryName": "US", "Marks": 56 }
    ],
    "Name": "John"
});
> db.demo418.insertOne({
    "details": [
        { "CountryName": "US", "Marks": 78 },
        { "CountryName": "UK", "Marks": 97 }
    ],
    "Name": "Mike"
});

2. Mục tiêu: Chỉ lấy user mà **TẤT CẢ** details đều có CountryName = "US"

-> Chỉ John thỏa (Mike có 1 UK).

3. Aggregation Pipeline phức tạp

> db.demo418.aggregate([
    { $unwind: "$details" },                           // Bật mảng details thành nhiều dòng
    {
        $group: {
            _id: "$_id",
            details: { $first: "$details" },           // Giữ 1 mẫu details
            alldetails: { $sum: 1 },                   // Tổng số phần tử details
            alldetailsmatch: {                         // Đếm phần tử có CountryName = "US"
                $sum: { $cond: [{ $eq: ["$details.CountryName", "US"] }, 1, 0] }
            },
            Name: { $first: "$Name" }
        }
    },
    {
        $project: {
            _id: 1, Name: 1, details: 1,
            arrayValue: { $cond: [{ $eq: ["$alldetails", "$alldetailsmatch"] }, 1, 0] }
        }
    },
    { $match: { arrayValue: 1 } }                      // Chỉ giữ where ALL match
]);

4. Kết quả

{ "_id": ObjectId("..."), "details": { "CountryName": "US", "Marks": 45 }, "Name": "John", "arrayValue": 1 }

Cách đơn giản hơn (MongoDB 3.6+): Dùng $not + $elemMatch (trong find)

Logic: "KHÔNG TỒN TẠI phần tử nào KHÔNG thỏa mãn" = "TẤT CẢ đều thỏa mãn".

db.demo418.find({
    $not: {
        details: { $elemMatch: { CountryName: { $ne: "US" } } }
    }
});

Cách này ngắn gọn, dễ hiểu, và dùng được index.