ใช้ $unwind และ $match เพื่อค้นหาในอาร์เรย์ออบเจกต์
เมื่อต้องการค้นหาเอกสารที่มีอาร์เรย์ของออบเจกต์ และต้องการคืนค่าเฉพาะออบเจกต์ที่ match (ไม่ใช่เอกสารทั้งชิ้น) ให้ใช้ Aggregation Pipeline พร้อม $unwind และ $match
ตัวอย่างข้อมูลทดสอบ
> db.demo414.insertOne({
"_id": "110",
"details": [
{ "StudentName": "John", "StudentMarks": 56 },
{ "StudentName": "Robert", "StudentMarks": 98 }
]
});
แสดงข้อมูล
> db.demo414.find();
ผลลัพธ์:
{ "_id": "110", "details": [{ "StudentName": "John", "StudentMarks": 56 }, { "StudentName": "Robert", "StudentMarks": 98 }] }
คำสั่ง Aggregation หา details ที่ StudentMarks = 56
> db.demo414.aggregate([
{ $unwind: "$details" },
{ $match: { "details.StudentMarks": 56 } }
]);
ผลลัพธ์ที่ได้
{ "_id": "110", "details": { "StudentName": "John", "StudentMarks": 56 } }
คำอธิบาย
- $unwind: "$details": แยกอาร์เรย์ details ให้แต่ละออบเจกต์กลายเป็นเอกสารแยกกัน (duplicate _id)
- $match: { "details.StudentMarks": 56 }: กรองเฉพาะเอกสารที่ StudentMarks = 56
- ผลลัพธ์คือเอกสารที่มี details เป็นออบเจกต์เดียว (ไม่ใช่อาร์เรย์)
กรณีต้องการคืนค่าเป็นอาร์เรย์กลับไป (MongoDB 3.6+)
> db.demo414.aggregate([
{ $match: { "details.StudentMarks": 56 } },
{ $project: { details: { $filter: { input: "$details", as: "d", cond: { $eq: ["$$d.StudentMarks", 56] } } } } }
]);
ใช้ $filter เพื่อกรองภายในอาร์เรย์โดยไม่ต้อง $unwind คืนค่าเป็นอาร์เรย์เหมือนเดิม
เปรียบเทียบ find() vs Aggregation
| find() | Aggregation ($unwind + $match) |
|---|---|
| คืนเอกสารทั้งชิ้น | คืนเฉพาะออบเจกต์ที่ match |
| ไม่สามารถกรองภายในอาร์เรย์ได้ | กรองภายในอาร์เรย์ได้ |
| เร็วกว่าสำหรับคิวรีง่าย | ยืดหยุ่นกว่า สร้างฟิลด์ใหม่ได้ |