MongoDB的文档验证功能为集合级别的数据完整性提供了保障,但一旦写入操作违反验证规则,数据库就会抛出Document failed validation错误。在部分版本或驱动中,这个异常对应的错误码显示为1900,也有环境将其报告为121。错误本身并不难理解,难的是搞清楚为什么验证规则会拦截本应合法的数据。很多情况下,问题并非出在validator表达式上,而是Validation level的取值过于严格。Validation level控制了验证器对插入和更新操作的生效范围,如果设置不当,即使规则写得合理,也会在特定写入场景下频繁触发1900。本文将围绕这一故障码展开,分析其与Validation level之间的关系,并给出排查和修复的具体方法。

故障码1900的本质:文档验证失败
错误码1900通常出现在执行insertOne、insertMany、updateOne或updateMany等写入操作时。它的直接含义是目标文档没有通过集合上定义的验证条件。MongoDB的验证器基于JSON Schema或查询表达式工作,只要文档中的字段类型、取值范围、必填项等不满足规则,数据库就会拒绝这次写入,并向客户端返回Document failed validation错误。这个错误码本身并不表示数据库或连接出现问题,而是业务数据与约束条件发生了冲突。
要触发这个错误,集合必须预先配置了validator。例如创建一个用户集合,要求姓名必须是字符串,年龄必须是大于等于18的整数,命令如下:
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "age"],
properties: {
name: { bsonType: "string" },
age: { bsonType: "int", minimum: 18 }
}
}
},
validationLevel: "strict"
});
在上述配置中,如果尝试插入一条年龄为16的文档,MongoDB就会返回错误码1900。到这里为止,大多数人都能理解验证失败的原因。但真实业务中经常出现这样的情况:验证规则本身并没有问题,报错文档以前也能正常写入,突然某次更新却触发了1900。这个时候就需要检查validationLevel的实际取值。它决定了验证器在什么写入场景下被强制执行,如果级别设置不当,会产生两种极端后果:要么验证形同虚设,要么严重阻碍正常的数据更新。
Validation level三个取值的行为差异
MongoDB为文档验证提供了三种级别:off、strict和moderate。默认级别是strict,这也是最容易触发1900的配置。在严格模式下,所有插入和更新操作都必须通过验证器,不管文档是新增的还是已经存在的,也不管文档原本是否满足验证条件。只要最终写入的文档不符合规则,操作就会被拒绝。这种一致性保障在数据治理严格的场景中很有价值,但对于已经存在大量历史数据的集合来说,它会成为一个棘手的问题。
moderate级别的行为则更加灵活。它对插入新文档完全不执行验证,只针对已经存在且当前有效的文档进行更新时做验证。如果一条文档原本就是无效的(例如在验证规则添加之前就已经存在,或者通过绕过验证的方式写入),那么对它的更新操作也不会触发验证。换句话说,moderate只负责防止有效文档被改坏,不会去纠正历史遗留的脏数据。最后,off级别会完全关闭验证,所有写入操作都不受约束,通常在临时迁移或修复数据时使用。
| 级别 | 插入新文档 | 更新有效文档 | 更新无效文档 |
|---|---|---|---|
| strict | 验证 | 验证 | 验证 |
| moderate | 不验证 | 验证 | 不验证 |
| off | 不验证 | 不验证 | 不验证 |
很多1900错误正是因为在默认的strict级别下,对历史无效文档执行了更新。这些文档可能在验证规则建立之前就已经存在,字段结构不符合新规则。当业务代码尝试更新它们的某个字段时,由于strict要求最终文档必须符合验证器,更新就会失败,即使这次更新本身的意图是合理的。把级别调整为moderate后,这类更新就不再被拦截,因为MongoDB只对原本就有效的文档进行验证。
定位与修复Validation level设置不当
当遇到故障码1900时,第一步应该查看集合的验证配置,而不是急着修改业务代码。可以使用db.getCollectionInfos命令获取集合的options信息,其中包含validator和validationLevel字段。在mongosh中执行如下命令:
db.getCollectionInfos({name: "users"});
输出结果中会显示类似validationLevel: "strict"的内容。如果确认验证器本身没有错误,而业务场景又需要更新历史无效文档,就可以通过collMod命令修改验证级别。下面的示例将users集合的验证级别改为moderate,同时保留原有的JSON Schema验证规则:
db.runCommand({
collMod: "users",
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "age"],
properties: {
name: { bsonType: "string" },
age: { bsonType: "int", minimum: 18 }
}
}
},
validationLevel: "moderate"
});
修改完成后,之前触发1900的更新操作就有可能成功执行。如果只是想临时解决某一条写入的失败问题,还可以在单次操作中使用bypassDocumentValidation选项绕过验证。该选项在insert和update命令中都可用,例如:
db.users.updateOne(
{ name: "Tom" },
{ $set: { age: 20 } },
{ bypassDocumentValidation: true }
);
需要提醒的是,bypassDocumentValidation只能作为临时手段使用。它会让写入的文档永久绕过验证,从而产生新的无效数据。如果频繁使用这个选项,验证规则就会失去意义。更稳妥的做法是从根本上调整validationLevel,或者修正验证器让它更贴合实际数据结构。
验证规则设计的最佳实践与预防措施
为了避免故障码1900频繁出现,在设计集合验证规则时就应该考虑历史数据和未来业务的兼容性。首先,尽量使用$jsonSchema来描述约束,因为它支持更细粒度的类型和条件判断。例如可以将某些字段设置为非必填,或者在特定条件下才要求某些字段存在。不要把验证器写得过于刚硬,否则后续增加字段或调整字段类型时都会遭遇写入失败。
其次,根据集合中数据的实际状态选择合适的validationLevel。如果集合刚刚创建并且所有写入都来自同一套应用代码,strict是合理的选择。但如果集合中已经存在不符合规则的历史数据,或者允许第三方系统写入部分不完整的数据,则应该考虑使用moderate。在数据修复阶段,可以临时切换到off,完成清理后再恢复为strict或moderate。
最后,建立定期审查验证配置的机制。业务需求变化会导致字段结构演进,验证器也需要同步调整。可以使用db.getCollectionInfos定期导出集合的验证配置,检查是否存在不再适用的规则。同时,结合应用层的数据校验逻辑,将数据库验证作为最后一道防线,而不是唯一的数据质量保障手段。这样既能减少1900错误对业务的影响,又能维持集合中数据的整体一致性。
MongoDB故障码1900Validation level文档验证修改时间:2026-09-19 17:44:05