การ 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()
คำอธิบายขั้นตอน:
$unwind: "$Details": แยก array Details เป็นหลายเอกสาร (1 element ต่อ 1 doc)$lookup: Join กับ demo102 โดย match Details.PId กับ _id$project: เลือก field ที่ต้องการ, ใช้ $arrayElemAt[0] เพราะ $lookup คืน array$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 อื่น