Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> MongoDB

MongoDB: ไม่สามารถคืนค่า Embedded Document เพียงอย่างเดียวได้ (แต่สามารถกรองได้)

MongoDB ไม่สามารถคืนค่าเฉพาะ Embedded Document ที่ตรงเงื่อนไขได้โดยตรง การ query บนอาร์เรย์ของ Embedded Document จะคืนเอกสารทั้งฉบับที่มี element ตรงเงื่อนไข

ตัวอย่างปัญหา

1. สร้างข้อมูลที่มีอาร์เรย์ของ Embedded Document

> db.queryToEmbeddedDocument.insertOne({
    "UserName": "Larry",
    "PostDetails": [
        {"UserMessage": "Hello", "UserLikes": 8},
        {"UserMessage": "Hi", "UserLikes": 6},
        {"UserMessage": "Good Morning", "UserLikes": 12},
        {"UserMessage": "Awesome", "UserLikes": 4}
    ]
});

2. พยายามค้นหาเฉพาะโพสต์ที่ UserLikes >= 8

> db.queryToEmbeddedDocument.find(
    {"PostDetails.UserLikes": { $gte: 8 }},
    { PostDetails: 1 }
).pretty();

3. ผลลัพธ์: คืนเอกสารทั้งหมด (รวมโพสต์ที่ Likes < 8)

{ 
    "_id" : ObjectId("..."), 
    "PostDetails" : [ 
        {"UserMessage": "Hello", "UserLikes": 8},
        {"UserMessage": "Hi", "UserLikes": 6},      // <-- ยังคงอยู่
        {"UserMessage": "Good Morning", "UserLikes": 12},
        {"UserMessage": "Awesome", "UserLikes": 4}   // <-- ยังคงอยู่
    ]
}

วิธีแก้: ใช้ Aggregation Pipeline ($unwind + $match)

db.queryToEmbeddedDocument.aggregate([
    { $unwind: "$PostDetails" },
    { $match: { "PostDetails.UserLikes": { $gte: 8 } } },
    { $project: { _id: 0, "PostDetails": 1 } }
]);

ผลลัพธ์ที่ถูกต้อง

{ "PostDetails" : { "UserMessage" : "Hello", "UserLikes" : 8 } }
{ "PostDetails" : { "UserMessage" : "Good Morning", "UserLikes" : 12 } }

สรุป

  • find() คืนเอกสารทั้งฉบับ ถ้ามี element ใด element หนึ่งในอาร์เรย์ตรงเงื่อนไข
  • ไม่สามารถ filter element ภายในอาร์เรย์ได้ด้วย find() ธรรมดา
  • ต้องใช้ Aggregation ($unwind → $match → $project) เพื่อกรอง element ในอาร์เรย์
  • MongoDB 3.6+ มี $filter ใน projection แต่ aggregation ยังยืดหยุ่นกว่า