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

MongoDB Aggregate: Join Array Field กับ Collection อื่นด้วย $lookup

การ join ข้อมูลระหว่าง array field ใน collection หนึ่งกับ _id ของอีก collection ใช้ $lookup ร่วมกับ $unwind เพื่อแยก array ก่อน join แล้วค่อยรวมกลับ

สร้าง Collection แรก (demo101) - มี Array Field

> db.demo101.insertOne({
  "_id" : "1",
  "Details" : [
    { "PId" : "200" },
    { "PId" : "201" },
    { "PId" : "201" }
  ]
});
{ "acknowledged" : true, "insertedId" : "1" }

สร้าง Collection ที่สอง (demo102) - ข้อมูลอ้างอิง

> db.demo102.insertOne({ "_id" : "201", "CustEmailId" : "Carol@gmail.com" });
{ "acknowledged" : true, "insertedId" : "201" }

(สมมติมีข้อมูล _id: "200" อยู่แล้ว: John@gmail.com)

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

> db.demo101.find();
// { "_id" : "1", "Details" : [ { "PId" : "200" }, { "PId" : "201" }, { "PId" : "201" } ] }

> db.demo102.find();
// { "_id" : "200", "CustEmailId" : "John@gmail.com" }
// { "_id" : "201", "CustEmailId" : "Carol@gmail.com" }

Query Aggregate: Join Array Field กับ Collection อื่น

> db.demo101.aggregate([
  { $unwind: "$Details" },
  { $lookup: {
    from: "demo102",
    localField: "Details.PId",
    foreignField: "_id",
    as: "out"
  }},
  { $project: {
    "_id": 1,
    "Details.PId": { $arrayElemAt: ["$out.CustEmailId", 0] }
  }},
  { $group: {
    _id: "$_id",
    Details: { $push: "$Details" }
  }}
]).pretty()

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

  1. $unwind: "$Details": แยก array Details เป็นหลายเอกสาร (1 element ต่อ 1 doc)
  2. $lookup: Join กับ demo102 โดย match Details.PId กับ _id
  3. $project: เลือก field ที่ต้องการ, ใช้ $arrayElemAt[0] เพราะ $lookup คืน array
  4. $group: รวมกลับเป็น array Details เดิม แต่ PId กลายเป็น Email แล้ว

ผลลัพธ์

{ "_id" : "1", "Details" : [
  { "PId" : "John@gmail.com" },
  { "PId" : "Carol@gmail.com" },
  { "PId" : "Carol@gmail.com" }
] }

PId 200 → John@gmail.com, PId 201 → Carol@gmail.com (ซ้ำ 2 ครั้ง)

ประโยชน์ของวิธีนี้

  • Normalization: เก็บแค่ reference (PId) ใน array, ข้อมูลจริงอยู่ collection แยก
  • หลีกเลี่ยงข้อมูลซ้ำ (denormalization)
  • อัปเดต email ที่เดียว แสดงผลทุกที่ที่ reference ไป

ตัวเลือก $lookup แบบ Pipeline (MongoDB 3.6+)

ยืดหยุ่นกว่า สามารถ filter, project ใน sub-pipeline:

db.demo101.aggregate([
  { $unwind: "$Details" },
  { $lookup: {
    from: "demo102",
    let: { pid: "$Details.PId" },
    pipeline: [
      { $match: { $expr: { $eq: ["$_id", "$$pid"] } } },
      { $project: { CustEmailId: 1, _id: 0 } }
    ],
    as: "out"
  }},
  { $unwind: "$out" },
  { $project: { _id: 1, "Details.PId": "$out.CustEmailId" }},
  { $group: { _id: "$_id", Details: { $push: "$Details" }}}
])

สรุป: $unwind → $lookup → $group = pattern มาตรฐานสำหรับ join array field กับ collection อื่น