MongoDB的设计哲学是模式自由,同一个集合里的文档可以拥有完全不同的字段结构。这种灵活性在开发初期非常方便,但一旦项目进入多人协作或对接多个业务方阶段,缺乏约束的数据就会变成隐患:某次写入把age写成了字符串,某个文档漏掉了user_id,下游统计脚本瞬间报错。其实MongoDB从3.6版本开始就提供了完善的Schema验证机制,可以在集合级别定义校验规则,把脏数据挡在写入阶段。本文将系统地讲解验证规则的定义方法。

一、什么是MongoDB的Schema验证机制
MongoDB的Schema验证本质上是附着在集合上的一个validator文档,它使用查询操作符或jsonSchema语法描述合法文档应该长什么样。当文档执行insert或update操作时,MongoDB会先检查文档是否满足验证规则,不满足则根据配置决定拒绝写入还是仅记录警告。
需要特别理解的一点是,MongoDB的验证是写入时校验,而不是像关系型数据库那样在建表时强制约束所有列。已存在的旧数据不会被自动修正,验证器只对发生写入操作的文档生效。这一点通过validationLevel参数控制,后面会详细展开。
验证器支持两种写法:一种是使用普通查询操作符如$type、$exists、$eq等组合条件;另一种是使用$jsonSchema操作符,这也是目前官方推荐的方式,语法与JSON Schema标准高度一致,表达能力更强,可读性也更好。
二、使用jsonSchema定义验证规则
$jsonSchema支持的关键字非常丰富,常用的包括required声明必填字段、bsonType指定字段类型、enum限定枚举值、minimum和maximum限定数值范围、pattern使用正则约束字符串格式、properties描述嵌套对象结构等。下面通过一个用户信息集合的完整示例来演示。
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["user_id", "name", "email", "age"],
properties: {
user_id: {
bsonType: "int",
description: "user_id必须是整数且必填"
},
name: {
bsonType: "string",
minLength: 2,
maxLength: 20,
description: "姓名必须是2到20位的字符串"
},
email: {
bsonType: "string",
pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
description: "必须符合邮箱格式"
},
age: {
bsonType: "int",
minimum: 0,
maximum: 150,
description: "年龄必须是0到150之间的整数"
},
status: {
enum: ["active", "disabled", "deleted"],
description: "状态只能是这三个值之一"
},
address: {
bsonType: "object",
required: ["city"],
properties: {
city: { bsonType: "string" },
street: { bsonType: "string" }
}
},
tags: {
bsonType: "array",
items: { bsonType: "string" }
}
}
}
}
})
这段规则做了几层约束:顶层要求user_id、name、email、age四个字段必填;email通过正则校验格式;address作为嵌套对象自身还有必填的city字段;tags数组要求每个元素都是字符串。description字段不会参与校验,但会出现在错误信息中,对排查问题非常有帮助,建议养成书写的习惯。
关于类型值有一个常见的坑:bsonType使用的是BSON类型而非JavaScript类型。比如数字1在shell中默认是double类型,如果规则声明为"int",插入{age: 1}会验证失败,必须写成NumberInt(1)或使用驱动程序指定整型。如果不关心具体数值类型,可以写成数组形式bsonType: ["int", "double", "long"]来兼容多种数字类型。
三、修改已有集合的验证规则
集合创建之后业务往往会变化,验证规则也需要随之调整。这时不能删除重建集合(数据会丢失),而要使用collMod命令修改现有集合的验证器。
db.runCommand({
collMod: "users",
validator: {
$jsonSchema: {
bsonType: "object",
required: ["user_id", "name", "phone"],
properties: {
user_id: { bsonType: "int" },
name: { bsonType: "string" },
phone: {
bsonType: "string",
pattern: "^1[3-9][0-9]{9}$",
description: "必须是中国大陆手机号"
}
}
}
},
validationLevel: "moderate"
})
注意collMod是整体替换验证器而不是增量合并,新的validator会完全覆盖旧规则,所以每次修改都要写完整的规则内容。修改完成后可以通过db.getCollectionInfos({name: "users"})查看当前生效的验证器配置,确认修改是否成功。
四、validationLevel与validationAction详解
这两个参数决定了验证器的作用范围和失败时的行为,是生产环境配置的关键。validationLevel有三个取值:strict是默认值,对所有insert和update操作都执行校验;moderate只对满足现有验证规则的文档执行update校验,以及对所有insert校验,适合存量数据不合规但又不希望阻塞更新的场景;off则完全关闭校验。
validationAction只有两个取值:error为默认值,验证失败直接拒绝写入并抛出Document failed validation错误;warn则只把验证失败记录到mongod日志中,写入照常成功。一个实用的上线策略是:初次给存量数据加验证规则时先设置warn观察一段时间日志,确认没有异常写入模式后再切回error,这样能避免规则过严导致业务写入瞬间大面积失败。
db.runCommand({
collMod: "users",
validationAction: "warn"
})
五、验证失败的排查技巧
当写入被拒绝时,MongoDB返回的错误信息中包含errInfo字段,其中failingDocumentId指出问题文档,details部分会具体说明哪个字段违反了哪条规则,配合前面写的description可以快速定位原因。
排查存量数据是否合规也有办法,可以直接把验证器条件当作查询条件来反向筛选违规文档:
// 查找email不符合校验规则的文档
db.users.find({
$or: [
{ email: { $exists: false } },
{ email: { $not: { $regex: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" } } }
]
})
总结一下,MongoDB的Schema验证机制在保持文档数据库灵活性的同时提供了关系型级别的数据约束能力。建议在项目初期就为新集合定义好jsonSchema规则,存量集合则采用warn模式渐进式收紧,让数据质量始终处于可控状态。需要注意验证器不能代替唯一索引,唯一性约束依然要靠unique index来实现,两者配合使用才能构建完整的数据完整性保障体系。
MongoDB Schema验证jsonSchemavalidator修改时间:2026-09-08 18:24:59