บทความนี้สาธิตการใช้ Aggregation Framework เพื่อกรอง (Filter) และ เรียงลำดับ (Sort) เอกสารตามค่าใน Subdocument (Object ใน Array) โดยเฉพาะกรองเฉพาะ Subdocument ที่ Age = 21 แล้วเรียงตาม StudentScore
สร้างข้อมูลตัวอย่าง
> db.sortBySubDocumentsDemo.insertOne(
{
"StudentName": "Chris",
"StudentDetails": [
{ "Age":21, "StudentScore":91 },
{ "Age":22, "StudentScore":99 },
{ "Age":21, "StudentScore":93 }
]
}
);
{ "acknowledged" : true, "insertedId" : ObjectId("5cd57e297924bb85b3f48942") }
> db.sortBySubDocumentsDemo.insertOne(
{
"StudentName": "Robert",
"StudentDetails": [
{ "Age":24, "StudentScore":78 },
{ "Age":21, "StudentScore":86 },
{ "Age":23, "StudentScore":45 }
]
}
);
{ "acknowledged" : true, "insertedId" : ObjectId("5cd57e4c7924bb85b3f48943") }
แสดงข้อมูลต้นฉบับ
> db.sortBySubDocumentsDemo.find().pretty();
{
"_id" : ObjectId("5cd57e297924bb85b3f48942"),
"StudentName" : "Chris",
"StudentDetails" : [
{ "Age" : 21, "StudentScore" : 91 },
{ "Age" : 22, "StudentScore" : 99 },
{ "Age" : 21, "StudentScore" : 93 }
]
}
{
"_id" : ObjectId("5cd57e4c7924bb85b3f48943"),
"StudentName" : "Robert",
"StudentDetails" : [
{ "Age" : 24, "StudentScore" : 78 },
{ "Age" : 21, "StudentScore" : 86 },
{ "Age" : 23, "StudentScore" : 45 }
]
}
Aggregation Pipeline: กรอง Age=21 แล้วเรียงตาม Score
> db.sortBySubDocumentsDemo.aggregate([
{ $match: { 'StudentDetails.Age': 21 } },
{ $unwind: '$StudentDetails' },
{ $match: { 'StudentDetails.Age': 21 } },
{ $project: { _id: 0, "StudentName": 1, 'StudentDetails.StudentScore': 1 } },
{ $sort: { 'StudentDetails.StudentScore': 1 } },
{ $limit: 5 }
]);
ผลลัพธ์
{ "StudentName" : "Robert", "StudentDetails" : { "StudentScore" : 86 } }
{ "StudentName" : "Chris", "StudentDetails" : { "StudentScore" : 91 } }
{ "StudentName" : "Chris", "StudentDetails" : { "StudentScore" : 93 } }
คำอธิบายแต่ละ Stage
- $match (Stage 1): กรอง Document 级别 - เก็บเอกสารที่มี อย่างน้อย 1 Subdocument ที่ Age=21
- $unwind: แยก Array
StudentDetailsให้แต่ละ Subdocument กลายเป็น Document แยกต่างหาก - $match (Stage 2): กรอง Subdocument 级别 - เก็บเฉพาะที่ Age=21 จริงๆ
- $project: เลือกฟิลด์ที่ต้องการ (StudentName, StudentScore) ซ่อน _id
- $sort: เรียงจาก Score น้อยไปมาก (1 = Ascending)
- $limit: จำกัดผลลัพธ์ 5 รายการ
จุดสำคัญ
ต้องใช้ $unwind ก่อนจึงจะ Filter/ Sort ได้ที่ระดับ Subdocument จริง มิฉะนั้น $match แรกจะ Filter ที่ระดับ Document (มี Subdocument ใด Subdocument หนึ่งตรงก็ผ่าน)