# MongoDB-Validation 類似 關係型數據庫(如 MySQL)為了資料完整性會設置約束條件,確保寫入的資料具有一致性以維持資料完整。 MongoDB 則是透過 Schema Validation 來進行資料結構的驗證。 ## 建立資料集合時建立 Validation 通過為集合指定 `validator` 屬性來指定資料的約束條件,例如: ```js db.createCollection("students", { validator: { $jsonSchema: { // 驗證器類型: JSON Schema bsonType: "object", title: "Student驗證", // 條件名稱 required: [ "name" , "age" , "major" ], // 必要欄位 properties: { // 分別指定各欄位驗證規則 name: { bsonType: "string", // 表示 name 是字串 description: "要有姓名,且須要是字串" // 錯誤提示內容 }, age: { bsonType: [ "string","int","null" ], // 表示 age 可以是 字串 數字 或者 null minimum: 18, // 限制數字範圍,但輸入字串時檢查會失效 maximum: 30, description: "本校入學年齡在 18~30 歲" }, courses: { bsonType: "array", // 表示 courses 必須是陣列 description: "修課表必須是陣列", items: { bsonType: "string", // 陣列的內容要求是字串 description: "課程名稱請以字串表示" } } } } }, validationLevel: "strict", // "strict" 或 "moderate" validationAction: "error" // "error" 或 "warn" } ) ``` ### JSON Schema 驗證 最直觀的驗證器就是通過撰寫 Schema 來描述集合中的每一筆資料(BSON)的格式。 - `required`: 必要欄位,以陣列形式表示 - `properties`: 描述各個屬性的格式 ```js $jsonSchema: { required: [ "name", "address" ], properties: { name: { bsonType: "string", }, address: { bsonType: "object", required: [ "zipcode" ], properties: { // JSON 允許巢狀結構,Schema 也可以巢狀描述 "street": { bsonType: "string" }, "zipcode": { bsonType: "string" } } } } } ``` #### 限定欄位的允許值 使用 enum 枚舉欄位允許值,避免欄位中輸入無效值。 ```js db.createCollection("sales", { validator: { $jsonSchema: { bsonType: "object", title: "驗證欄位是否為允許值", properties: { type: { // 這個欄位要通過 enum 驗證,只允許特定值 enum: [ "中杯", "大杯", "超大杯" ], description: "不好意思我們沒有賣小杯" } } } } } ) ``` #### 禁止額外欄位 避免集合中的文檔出現新欄位,可以限制欄位必須為 `properties` 所指定的欄位,可以避免像是因為輸入錯誤而新增欄位。 ```js db.createCollection("users", { validator: { "$jsonSchema": { "required": [ "username", "password" ], "properties": { "_id": { "bsonType": "objectId" }, "username": { "bsonType": "string" }, "password": { "bsonType": "int" } }, "additionalProperties": false // 不允許添加額外欄位 } } }) ``` ### 查詢表達式驗證 `$expr` 可以使用 MongoDB 特有的查詢表達式來表達在進行資料儲存前應該先進行的驗證運算。 ```js db.createCollection( "orders", { validator: { $expr: { // 使用表達式進行驗證 $eq: [ // 用查詢表達式描述驗證規則 "$total", // 總價是價格乘以數量 { $multiply: [ "$price", "$quantity" ] } // $ 符號表示引用文檔中的欄位 ] } } } ) ``` #### 多條件驗證 使用 `$and` 表達多條件驗證: ```js db.createCollection("sales", { validator: { "$and": [ // $and 表示需要通過多個條件驗證 { "$expr": { // 使用表達式進行數據驗證 "$lt": ["$discountedPrice", "$price"] // 要求折扣價需要小於原價 } }, { "$jsonSchema": { required: [ "discountedPrice" , "price" ], } } ] } } ) ``` ### 驗證強度 ValidationLevel 預設的驗證強度,是針對**每一筆**插入與更新都進行檢查 (預設"strict") 但若是添加驗證之前就已經存在舊資料,因為舊資料可能採取不同的規則管理,預設模式就會造成舊資料無法照舊規則更新。 因此選擇 "moderate" 和緩模式,就不會對不符合驗證的舊資料進行驗證,但是已經符合驗證的舊資料則不能修改為不符合驗證的狀態。(很人性化的說) 除非是完全不希望驗證器進行任何阻擋,否則不會設置 `validationAction: "warn"` ## 修改集合的 Validation ```js db.runCommand( { collMod: "students", // 使用 runCommand 來修改 collMod 指定集合的驗證 validator: { $jsonSchema: { // 新的驗證條件內容... } } } ) ``` > [!🚨] > 修改後的規則不朔及既往。只驗證新加入的資料。 ## 查看集合的 Validation 查看目前 db 中的 collection 資訊使用 `getCollectionInfos()` 方法: ```js db.getCollectionInfos( { name: "students" } ) // 可以查看名為 "students" 的 collection 資訊 ``` ```js db.getCollectionInfos( { name: "students" } )[0].options.validator // 指定查看其 validator 資訊 ``` ## 查找符合/不符合 Schema 的資料 將 Schema 存到變數,用來匹配查找資料: ```js let myschema = { $jsonSchema: { required: [ "name", "age" ], properties: { name: { bsonType: "string" }, age: { bsonType: "int" } } } } ``` 查找匹配: ```js db.inventory.find(myschema) ``` 查找不匹配: ```js db.inventory.find( { $nor: [ myschema ] } ) ``` 如此,就可以此條件去進行 `$set` 資料更新,或者 delete 指令的刪除。 ## 繞過驗證 有些特殊的狀況可能會需要強制繞過現有的資料驗證模式,比如說,進行資料的災難還原時使用了舊資料...等。 ```js db.runCommand( { // 使用 runCommand 而非 insert insert: "importantOrders", documents: [ // 要插入的舊資料的內容 ], bypassDocumentValidation: true // <- 強制繞過驗證的指令 } ) ``` ## 其他資料完整性約束 ### 唯一值(Unique Indexes) 透過 創建索引 的方式來保證欄位的唯一性。 ```js db.users.createIndex({ firstname: 1, lastname: 1 }, { unique: true }) ``` ### 引用完整性(Reference Integrity) 由於 MongoDB 沒有外鍵約束,因此需要手動維護資料參考的完整性。 例如:刪除用戶時 `find()` 檢查有無該用戶的訂單。