ข้ามไปยังเนื้อหา

การ Query ใน MongoDB

การใส่ข้อมูลเข้าไปนั้นง่าย ทักษะที่แยกคนที่แค่ เก็บ document ออกจากคนที่ใช้ฐานข้อมูลได้จริงคือการอ่านข้อมูลกลับมาอย่างแม่นยำ การ query คือศิลปะของการอธิบายอย่างเจาะจงและไม่เกินจำเป็นว่าคุณสนใจ document ชุดย่อยกลุ่มไหน — แล้วจัดรูปสิ่งที่ส่งกลับมาให้คุณได้รับเฉพาะ field ที่ต้องการ ในลำดับที่ต้องการ แบ่งเป็นหน้าเล็กพอที่จะจัดการได้ โมดูลนี้พูดถึงครึ่งหลังของฐานข้อมูลทั้งหมด นั่นคือการอ่าน

เราจะทำงานกับ collection จำลองเล็ก ๆ ของสมาชิกห้องสมุด members และหนังสือที่พวกเขายืม เพราะข้อมูลขนาดเล็กช่วยให้ query เป็นจุดสนใจ เมื่อจบบทคุณจะสามารถ filter ด้วยการเปรียบเทียบและการรวม logic เจาะลึกเข้าไปใน array และ sub-document ที่ฝังอยู่ พร้อมทั้งตัด sort และแบ่งหน้าผลลัพธ์ได้โดยไม่ต้องลาก collection ทั้งก้อนข้ามสายไป

flowchart LR
  Q["Your query"] --> F["Filter: which documents"]
  Q --> P["Projection: which fields"]
  Q --> O["Order: sort"]
  Q --> Pg["Page: limit and skip"]
  F --> R["Result cursor"]
  P --> R
  O --> R
  Pg --> R
  R --> App["Your app"]
คำขออ่านคือการตัดสินใจสี่เรื่องที่เป็นอิสระต่อกัน — document ไหน, field ไหน, ลำดับอย่างไร, จำนวนเท่าไร — ที่รวมกันเป็น cursor เดียว

เมื่อคุณ query MongoDB คุณไม่เคยเขียนลูป คุณส่ง filter document ให้ server ที่ อธิบาย document ที่คุณต้องการ แล้ว server จะหาให้ filter อย่างด้านล่างอ่านได้ว่า “สมาชิกทุกคนที่เข้าร่วมในปี 2023 และตอนนี้ไม่ค้างค่าปรับเลย”

db.members.find({ joined: 2023, fines: 0 })

filter ที่เรียบง่ายที่สุดจับคู่ชื่อ field กับค่าหนึ่งค่า และหลายคู่ใน filter เดียวกันคือ AND โดยปริยาย จากจุดนั้นการ query เติบโตได้สี่ทิศทาง และโมดูลนี้จะพาไปทีละทิศทาง

  • Comparison และ logical operator มากกว่าการจับคู่แบบตรงเป๊ะ: มากกว่า, น้อยกว่า, การเป็นสมาชิกในเซ็ต, การไม่มี field และวิธีรวมเงื่อนไขด้วย AND/OR/NOT แบบชัดเจน เหล่านี้คือเครื่องมือใช้งานประจำวัน
  • การ query array field หนึ่งสามารถเก็บ list ได้ คุณจะได้เรียนการจับคู่ element เดียว การบังคับหลายเงื่อนไขบน element เดียวกัน ด้วย $elemMatch การบังคับให้มีหลายค่าครบทุกค่าด้วย $all และการ filter ตามความยาวของ array
  • การ query nested document document ซ้อนกันได้ dot notation ช่วยให้คุณเข้าถึง field ที่ฝังลึกอยู่ใน object และมีความแตกต่างสำคัญระหว่างการจับคู่ทั้ง sub-document แบบตรงเป๊ะกับการ match แค่ field เดียวข้างใน
  • Projection, การ sort และการแบ่งหน้า เมื่อคุณได้ document ที่ถูกต้องแล้ว คุณตัดสินใจว่าจะส่ง field ไหนกลับมา จะมาในลำดับใด และจะแบ่งผลลัพธ์ก้อนใหญ่ออกเป็นหน้าที่จัดการได้อย่างไร — และทำไมลูกเล่นการแบ่งหน้ายอดนิยมตัวหนึ่งถึงช้าลงเมื่อข้อมูลใหญ่ขึ้น

ทุกบทเรียนแสดง query เดียวกันห้าแบบ — ใน mongosh แล้วตามด้วย driver ของ Node.js, Python, Go และ Rust — คู่กับ JSON document ที่จะส่งกลับมา เพื่อให้คุณอ่านรูปร่างของคำตอบได้ ไม่ใช่แค่ตัวคำสั่ง

ตัวเลือกBenefitCost
Filter อย่างเดียว ไม่ระบุ projectionเขียนเร็ว ใช้ query ได้ทันทีส่ง field ที่ไม่ได้ใช้กลับมาทุกครั้ง เปลืองแบนด์วิดท์และ memory ฝั่ง client
Filter + projection ที่ระบุ field ชัดเจนลดขนาดข้อมูลที่ส่งผ่านเครือข่าย บังคับให้ทีมเห็นรายการ field ที่แอปพึ่งพาจริงต้องดูแล field list ให้ตรงกับ schema ที่เปลี่ยนไปเรื่อย ๆ
filter แบบจับคู่ตรงเป๊ะเร็วและ index ได้เสมอตอบคำถามเชิงช่วง/เซ็ต/นิพจน์ซับซ้อนไม่ได้
filter ที่ซับซ้อนด้วย operator หลายตัวและ $and/$orตอบคำถามได้ละเอียดขึ้นมากอ่านยากขึ้น และเสี่ยงบังคับให้ query planner เลือก index ที่ไม่เหมาะสม
  • ใช้ $where เพื่อเขียนเงื่อนไขที่ operator มาตรฐานทำไม่ได้$where รัน JavaScript ต่อ document จึงช้ามาก ใช้ index ไม่ได้เลย และเปิดช่องให้เกิดปัญหาด้าน security หากรับ input จากผู้ใช้ ให้มองหา operator มาตรฐานหรือ aggregation ก่อนเสมอ
  • เขียน regex แบบไม่ยึด anchor เช่น /foo/ แทน /^foo/ — regex ที่ไม่มี ^ บังคับให้ MongoDB สแกนทุก document ในคอลเลกชัน ใช้ index ไม่ได้แม้ field นั้นจะมี index อยู่ก็ตาม ถ้าต้องการค้นแบบ “ขึ้นต้นด้วย” ให้ anchor ที่จุดเริ่มต้นเสมอ
  • คิดว่าการไม่ระบุ projection ไม่มีต้นทุน — ทุก field ที่ไม่ได้ตัดออกจะวิ่งข้ามเครือข่ายมา แล้วฝั่งแอปต้อง serialize ทั้งหมด สำหรับ document ที่มี field ใหญ่ (description ยาว ๆ หรือ array ขนาดใหญ่) การลืม projection คือสาเหตุ latency ที่พบบ่อย

💡 ตัวอย่างจากของจริง

หน้า filter สินค้าของอีคอมเมิร์ซ — sidebar ที่กรองตามหมวดหมู่ ช่วงราคา แบรนด์ และ rating ผูกตรงกับ comparison/logical operator ($gte, $lte, $in) และใช้ projection ส่งกลับเฉพาะ field ที่การ์ดสินค้าต้องใช้ (ชื่อ ราคา รูปหลัก) ไม่ใช่ document เต็ม

แผง search ฝั่ง admin — เครื่องมือค้นหาผู้ใช้หรือ order ภายในองค์กรมักเผลอเปิดช่องให้ผู้ใช้พิมพ์ regex เองแล้วส่งตรงเข้า query โดยไม่ anchor ทำให้หน้าค้นหาช้าลงเรื่อย ๆ เมื่อข้อมูลโตขึ้น

ใน MongoDB query filter คืออะไร?
filter ที่มีคู่ field/value สองคู่ เช่น joined และ fines หมายความว่าอย่างไร?
ข้อใดไม่ใช่หนึ่งในสี่การตัดสินใจที่คำขออ่านรวมเข้าด้วยกัน?