MongoDB不提供内置强schema校验,需结合JSON Schema validator(如$bsonType、required)与应用层工具(如Zod)双保险:前者拦截非法结构,后者处理业务逻辑,缺一不可。

用 JSON Schema 进行插入前校验
MongoDB 从 3.6 开始支持在集合上设置 validator,本质是用 JSON Schema 定义字段白名单、类型、必填项等规则。它只在 insertOne()、updateOne() 等写操作时触发校验,不干预读取。
常见错误现象:直接 db.collection.insertOne({age: "25", name: null}) 成功写入,但业务期望 age 是数字、name 非空 —— 这类问题靠 validator 能拦截。
- 必须用
runCommand或createCollection显式启用,不能对已有集合直接加 validator(除非先drop) - schema 中字段名区分大小写,且
$jsonSchema下的required列表只检查顶层字段,嵌套文档需用properties层层定义 - 不支持正则校验字段名是否含
.或$—— 这类命名违规需靠应用层过滤,validator 对字段名本身无约束力
示例:只允许 name(字符串)、age(整数)、tags(字符串数组),且 name 必填:
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name"],
properties: {
name: { bsonType: "string" },
age: { bsonType: "int" },
tags: {
bsonType: "array",
items: { bsonType: "string" }
}
}
}
}
})
用应用层 Schema 库统一约束
Node.js 用 zod 或 joi、Python 用 pydantic,在调用 insertOne() 前做完整校验。比 JSON Schema 更灵活,能处理业务逻辑(如 “email 必须带 @”、“password 长度 ≥8”)。
容易踩的坑:
- 忘记在所有插入入口(API、后台任务、脚本)都套同一套校验逻辑,漏掉一个就等于绕过全部约束
- 校验库返回的错误信息太泛(如 “validation failed”),没包含具体字段和原因,排查成本高
- 把校验逻辑写在 controller 层,但 CLI 工具或 mongosh 直连仍可绕过 —— 这类场景必须靠 JSON Schema 补位
为什么不能依赖 _id 或索引实现字段限制?
_id 字段只是主键,不参与字段结构校验;唯一索引(createIndex({email: 1}, {unique: true}))只防重复值,不管字段是否存在、类型是否正确。
典型误用:
- 以为建了
{name: 1}索引就能阻止插入不含name的文档 —— 实际上可以插入{_id: ObjectId(), score: 95},索引字段缺失时默认为null,照样成功 - 给
age加唯一索引,结果插入{age: null}多次 —— MongoDB 允许多个null值共存,唯一性不生效
字段名和嵌套层级的实际限制
即使没设 validator,MongoDB 本身对字段名和结构也有硬性约束,这些限制会影响你“想限制什么”:
- 字段名不能含
\0(空字符),否则驱动会截断;含.或$虽然允许,但在查询、更新中会被解析为路径操作符(如user.name),极易出错 - 单文档最大 16MB,嵌套深度上限 100 层 —— 如果业务允许任意深嵌套,很快就会撞到这个天花板
- 重复字段名(如
{a: 1, a: 2})属于非法 BSON,部分驱动静默丢弃后一个,部分插入后查询行为不可预测,validate命令 +full: true可检测,但最好从源头杜绝
真正难的不是写几条校验规则,而是让所有写入路径(包括运维脚本、旧系统迁移、第三方工具同步)都经过同一道门。一旦漏掉,schema 就形同虚设。

















