การดึงข้อมูลเฉพาะ nested object ที่ตรงเงื่อนไขใน MongoDB สามารถทำได้ด้วย findOne() ร่วมกับ projection ที่ใช้ $elemMatch เพื่อกรอง element ใน array
สร้างคอลเลกชันและข้อมูลตัวอย่าง (โครงสร้างซ้อน 3 ระดับ)
> db.demo109.insertOne({
"Name" : "Chris",
"Subjects" : [
{
"Id" : "100",
"Name": "MySQL",
"InstructorDetails" : [
{ "Name" : "John" }
]
},
{
"Id" : "101",
"Name": "MongoDB",
"InstructorDetails" : [
{ "Name" : "Mike" }
]
}
]
});
{ "acknowledged" : true, "insertedId" : ObjectId("5e2ee7df9fd5fd66da21447a") }
แสดงข้อมูลทั้งหมด
> db.demo109.find();
ผลลัพธ์: เอกสารเดียว มี Subjects array 2 elements แต่ละ element มี InstructorDetails array
Query ดึงเฉพาะ Subject ที่ Id="100" (MySQL)
> db.demo109.findOne(
{ Name: "Chris" },
{ 'Subjects': { $elemMatch: {'Id': "100"} } }
);
คำอธิบาย:
findOne: คืนค่าเอกสารเดียว (ตัวแรกที่เจอ)- Filter:
{Name: "Chris"}- หาเอกสารหลัก - Projection:
{'Subjects': {$elemMatch: {'Id': "100"}}}- ใน array Subjects ให้คืนเฉพาะ element ที่ Id="100"
ผลลัพธ์
{ "_id" : ObjectId("5e2ee7df9fd5fd66da21447a"),
"Subjects" : [
{ "Id" : "100", "Name" : "MySQL", "InstructorDetails" : [ { "Name" : "John" } ] }
]
}
ได้เฉพาะ Subject MySQL พร้อม InstructorDetails ข้างใน ไม่รวม Subject MongoDB
การใช้ Aggregate สำหรับกรณีซับซ้อนกว่า
หากต้องการ filter หลายระดับ หรือ transform ข้อมูล ให้ใช้ aggregate:
db.demo109.aggregate([
{ $match: { Name: "Chris" }},
{ $unwind: "$Subjects" },
{ $match: { "Subjects.Id": "100" }},
{ $project: { _id: 0, Subject: "$Subjects" }}
])
กรณีใช้ findOne กับ Callback (Node.js Driver)
db.demo109.findOne(
{ Name: "Chris" },
{ 'Subjects': { $elemMatch: {'Id': "100"} } },
function (err, doc) { console.log(doc) }
);
สรุป: findOne + $elemMatch projection = วิธีง่ายที่สุดสำหรับดึง nested object เฉพาะส่วน | aggregate = ยืดหยุ่นกว่าสำหรับ transformation