ซ่อนฟิลด์ซ้อนในออบเจกต์ด้วย $objectToArray, $project, $arrayToObject
MongoDB ไม่รองรับ wildcard projection (เช่น "details.*.Age": 0) โดยตรงใน find() แต่สามารถทำได้ผ่าน Aggregation Pipeline โดยแปลงออบเจกต์เป็นอาร์เรย์ กรอง แล้วแปลงกลับ
ตัวอย่างข้อมูลทดสอบ
> db.demo413.insertOne({
"_id": "101",
"details": {
"Info1": { "Name": "Chris", "Age": 21 },
"Info2": { "Name": "David", "Age": 23 }
}
});
แสดงข้อมูลก่อน
> db.demo413.find();
ผลลัพธ์:
{ "_id": "101", "details": { "Info1": { "Name": "Chris", "Age": 21 }, "Info2": { "Name": "David", "Age": 23 } } }
คำสั่ง Aggregation ซ่อนฟิลด์ Age ในทุก Sub-object
> db.demo413.aggregate([
{ $project: { "details": { $objectToArray: "$details" } } },
{ $project: { "details.v.Age": 0 } },
{ $project: { "details": { $arrayToObject: "$details" } } }
]);
ผลลัพธ์ที่ได้
{ "_id": "101", "details": { "Info1": { "Name": "Chris" }, "Info2": { "Name": "David" } } }
คำอธิบายขั้นตอน
- $objectToArray: "$details": แปลงออบเจกต์ details เป็นอาร์เรย์ของ key-value pairs:
[{k: "Info1", v: {Name: "Chris", Age: 21}}, {k: "Info2", v: {Name: "David", Age: 23}}] - { "details.v.Age": 0 }: ในโครงสร้างใหม่ details เป็นอาร์เรย์ v คือค่า (sub-object) ซ่อน Age ในทุก v
- $arrayToObject: "$details": แปลงอาร์เรย์กลับเป็นออบเจกต์
ข้อควรระวัง
- วิธีนี้ใช้ได้กับออบเจกต์ระดับหนึ่ง (One level deep)
- ถ้ามีหลายระดับ ต้อง $unwind แล้วทำซ้ำ หรือใช้ $function (MongoDB 4.4+)
- มีค่าใช้จ่ายด้านประสิทธิภาพ (ต้องแปลงข้อมูล) เหมาะสำหรับข้อมูลไม่มาก หรือการรายงาน
ทางเลือกอื่น: ใช้ $project กำหนดฟิลด์ที่ต้องการแทน (Inclusion)
> db.demo413.aggregate([
{ $project: { _id: 1, "details.Info1.Name": 1, "details.Info2.Name": 1 } }
]);
แต่ต้องระบุ path ทุกตัวด้วยมือ