Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> MongoDB

การกรองเอกสารที่ทุกองค์ประกอบในอาร์เรย์ซ้อนตรงตามเงื่อนไขใน MongoDB

กรองเอกสารที่ทุกสมาชิกใน Nested Array ผ่านเงื่อนไขด้วย Aggregation

เมื่อต้องการหาเอกสารที่ ทุกองค์ประกอบ ในอาร์เรย์ซ้อน (Nested Array) ตรงตามเงื่อนไข (เช่น CountryName = "US" ทุกตัว) ให้ใช้ Aggregation Pipeline พร้อม $unwind, $group, และ $cond

ตัวอย่างข้อมูลทดสอบ

> 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"
});

แสดงข้อมูลทั้งหมด

> db.demo418.find();

ผลลัพธ์:

{ "_id": ObjectId("..."), "details": [{ "CountryName": "US", "Marks": 45 }, { "CountryName": "US", "Marks": 56 }], "Name": "John" }
{ "_id": ObjectId("..."), "details": [{ "CountryName": "US", "Marks": 78 }, { "CountryName": "UK", "Marks": 97 }], "Name": "Mike" }

คำสั่ง Aggregation กรองเอกสารที่ details ทุกตัวเป็น US

> db.demo418.aggregate([
  { $unwind: "$details" },
  { $group: {
    _id: "$_id",
    details: { $first: "$details" },
    alldetails: { $sum: 1 },
    alldetailsmatch: { $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 } }
]);

ผลลัพธ์ที่ได้ (เฉพาะ John ที่ทุก details เป็น US)

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

คำอธิบายขั้นตอน

  1. $unwind: "$details": แยกอาร์เรย์ details ให้แต่ละออบเจกต์เป็นเอกสารแยกกัน
  2. $group: รวมกลับตาม _id เดิม นับจำนวนทั้งหมด (alldetails) และนับที่ match US (alldetailsmatch)
  3. $project: สร้าง arrayValue = 1 ถ้า alldetails == alldetailsmatch (ทุกตัว match)
  4. $match: { arrayValue: 1 }: กรองเอกสารที่ทุกตัว match

วิธีที่ง่ายกว่า (MongoDB 3.6+): ใช้ $all หรือ $not + $elemMatch

> // หาเอกสารที่ details ทุกตัวเป็น US
> db.demo418.find({ "details.CountryName": { $not: { $elemMatch: { $ne: "US" } } } });

หรือใช้ $all สำหรับค่าที่ต้องมีทั้งหมด (แตกต่างจาก "ทุกตัวเป็นค่านั้น")