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

Schema validation

MongoDB เป็นแบบ schema-flexible โดยปริยาย: collection จะยอมรับ document ที่มีรูปร่างต่างกันอย่างมากอย่างเต็มใจ ซึ่งให้อิสระระหว่างการ prototype และอันตรายใน production Schema validation ให้คุณเลือกกลับเข้าสู่การบังคับใช้ — เพื่อประกาศ สำหรับ collection หนึ่ง ว่า field ใดต้องมี ต้องเป็นชนิดใด และค่าใดที่อนุญาต — โดยไม่ต้องทิ้งความยืดหยุ่นของ document model ในจุดที่ยังต้องใช้ คุณแนบกฎผ่าน $jsonSchema validator แล้ว server จะตรวจทุกการเขียนเทียบกับกฎนั้น ถือเป็นราวกันตกที่ค่อยเพิ่มทีหลังตอนรูปร่างของ collection ลงตัวแล้ว

validator คือ query expression ที่ทุก document ต้องเป็นไปตาม operator $jsonSchema เป็นวิธีมาตรฐานในการเขียน สร้างขึ้นจาก keyword หลักสามตัว:

  • bsonType — ชนิดที่ field (หรือ document) ต้องเป็น เช่น "object", "string", "int" หรือ "array"
  • required — array ของชื่อ field ที่ต้องมีอยู่
  • properties — map ต่อ field ที่อธิบายชนิดและข้อจำกัดของแต่ละ field

นี่คือ validator สำหรับ members collection: สมาชิกทุกคนต้องมี name เป็น string และปี joined เป็น integer ที่อย่างน้อย 1900 และถ้ามี fines ก็ต้องเป็นตัวเลขที่ไม่ติดลบ

flowchart LR
  W["Incoming write"] --> V{"Matches $jsonSchema?"}
  V -->|"Yes"| A["Accepted and stored"]
  V -->|"No, action error"| R["Rejected with a validation error"]
  V -->|"No, action warn"| L["Stored but logged as a warning"]
ทุกการเขียนต้องผ่าน validator ก่อน แล้ว validationAction ตัดสินว่าจะ reject ทิ้งหรือแค่บันทึก log

การแนบ validator เป็นส่วนหนึ่งของการสร้าง collection การเรียกด้านล่างสร้าง members ด้วยกฎที่อธิบายไว้ข้างต้น นี่เป็น operation จริง จึงดูแตกต่างกันเล็กน้อยใน shell และในแต่ละ driver:

db.createCollection("members", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "joined"],
properties: {
name: { bsonType: "string" },
joined: { bsonType: "int", minimum: 1900 },
fines: { bsonType: "number", minimum: 0 }
}
}
},
validationLevel: "strict",
validationAction: "error"
})

document ที่ผ่านครบทุกกฎจะบันทึกลงไปได้เลย ไม่มีเสียงบ่น:

{ "name": "Ada", "joined": 2024, "fines": 0 }

ส่วน document ที่ผิดกฎจะโดน reject อันนี้ขาด field joined ที่ required และใส่ name ผิด type การเขียนจึงล้มเหลวด้วย validation error แทนที่จะบันทึกเงียบ ๆ:

{ "name": 42 }

มีปุ่มสองตัวควบคุมว่ากฎจะกัดแรงแค่ไหน validationLevel ตัดสินว่าจะตรวจ การเขียนแบบไหนบ้าง: "strict" (ค่าปริยาย) validate ทุก insert และ update ส่วน "moderate" validate insert และ update ต่อ document ที่เป็นไปตาม schema อยู่แล้ว แต่ปล่อย document ที่ invalid เดิมไว้เฉย ๆ — มีประโยชน์เมื่อคุณเพิ่ม validation ให้กับ collection ที่มีข้อมูลรกอยู่แล้ว validationAction ตัดสินว่าจะเกิดอะไรขึ้นเมื่อล้มเหลว: "error" (ค่าปริยาย) ปฏิเสธการเขียน ส่วน "warn" อนุญาตให้เขียนแต่บันทึก warning ไว้ใน server log — วิธีอ่อนโยนในการค้นหาการละเมิดโดยไม่ทำให้การเขียนพังระหว่างการ migration

การ rollout ที่พบบ่อยคือเริ่มด้วย validationAction: "warn" และ validationLevel: "moderate" เฝ้าดู log เพื่อเรียนรู้ว่าข้อมูลเดิมละเมิดกฎใหม่มากแค่ไหน แก้ไขตัวที่ผิด แล้วค่อยเข้มขึ้นเป็น "error" และ "strict"

ใน Compass: เปิด collection แล้วไปที่แท็บ Validation คุณสามารถวางหรือแก้ไข document $jsonSchema ที่นั่น เลือก validation level และ action จาก dropdown และ Compass จะแสดงตัวอย่างว่า document เดิมตัวใดจะผ่านหรือไม่ผ่านก่อนที่คุณจะบันทึกกฎ — วิธีปลอดภัยในการทดสอบ validator กับข้อมูลจริง

  • Validation เป็นแบบ opt-in และต่อ collection collection ที่ไม่มี validator ทำงานเหมือนเดิมทุกประการ — ไม่มีการบังคับใช้อะไร
  • bsonType เข้มงวดกว่าการจัดการตัวเลขแบบหลวม ๆ ของ JSON: "int", "long", "double" และ "decimal" แยกจากกัน ถ้า driver ส่งค่าเป็น double ในที่ที่คุณ required เป็น int การเขียนจะล้มเหลว — เป็นเรื่องที่ทำให้แปลกใจกันบ่อย
  • required ตรวจแค่ว่า field มีอยู่ เท่านั้น ส่วนกฎราย field ใน properties ตรวจ type และค่า ใช้ทั้งสองด้วยกันเพื่อปักหมุด field ให้แน่นสมบูรณ์
  • ใช้ "moderate" บวก "warn" เมื่อเพิ่ม validation ให้กับ collection ที่มีอยู่ เพื่อที่คุณจะไม่ทำให้การเขียนต่อ document ที่มีมาก่อนกฎพัง
  • คุณสามารถเปลี่ยนหรือลบ validator ทีหลังด้วยคำสั่ง collMod — validation ไม่ใช่ประตูทางเดียว
ตัวเลือกBenefitCost
ใช้ $jsonSchema validationจับการเขียนที่ผิดรูปแบบได้ตั้งแต่ระดับ database เหมือนมี schema แบบเบา ๆ คอยกันตกเพิ่มขั้นตอนตอนเขียน ต้องคอย sync กฎกับโค้ดฝั่งแอปพลิเคชันเสมอ
ไม่ใช้ validator (พึ่ง schema flexibility เดิมของ MongoDB)ยืดหยุ่นเต็มที่ เขียนอะไรก็ได้โดยไม่มีแรงเสียดทานข้อมูลผิดรูปแบบหลุดเข้า production ได้ง่าย กว่าจะรู้ก็สายเกินไปแล้ว
  • คิดว่า $jsonSchema validator แทนที่การ validate ฝั่งแอปพลิเคชันได้ทั้งหมด — validator เช็คแค่ shape พื้นฐานตอนเขียนเข้า database เท่านั้น ไม่รู้ business rule เชิงลึก เช่น “email นี้ต้องไม่ซ้ำกับ user อื่น” ยังต้องใช้ unique index หรือ logic แยกต่างหาก
  • เพิ่ม validator เข้ม ๆ ทันทีบน collection ที่มีข้อมูลเก่าอยู่แล้ว — ถ้าไม่ผ่าน "moderate" กับ "warn" ก่อน การเปิด "strict" กับ "error" ทันทีจะทำให้การเขียนต่อ document เก่าที่ไม่ตรง schema ล้มเหลวโดยไม่ทันตั้งตัว
  • ลืมอัปเดต validator เมื่อ schema ของแอปพลิเคชันเปลี่ยน — validator ไม่ sync กับโค้ดอัตโนมัติ ถ้าเพิ่ม field ใหม่ในแอปแต่ลืมแก้ $jsonSchema การเขียนอาจถูกปฏิเสธโดยไม่คาดคิด

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

Toyota Connected — validate payload telemetry จากรถยนต์ที่เชื่อมต่อด้วย $jsonSchema ก่อนเก็บ เพื่อกัน sensor ที่ส่งข้อมูลผิดรูปแบบไม่ให้ปนเข้าไปใน collection หลัก

ระบบธนาคารและ fintech — ใช้ $jsonSchema เป็นราวกันตกบน collection ธุรกรรม กำหนด required field อย่างจำนวนเงินและสกุลเงินให้ตรง type เสมอ ก่อนที่ข้อมูลผิดจะไหลต่อไปยัง downstream analytics

operator ใดแสดง schema validation rule ของ MongoDB?
keyword required ใน $jsonSchema validator ทำอะไร?
validator ตั้ง validationAction เป็น "warn" แล้ว document ที่ผิดกฎจะเป็นอย่างไร?
ทำไมคุณอาจเลือก validationLevel "moderate" เมื่อเพิ่มกฎให้กับ collection ที่มีอยู่?