既然我们已经了解了 movies 文档的结构,就使用 $jsonSchema 创建一个新集合来强制一些基本规则。我们将从几个常见约束开始:
title:必须为字符串,且为必填
year:必须为整数,且为必填
genres:必须为字符串数组
runtime:必须为正整数
我们将该新集合命名为 validated_movies。
创建集合
运行以下命令以带验证地创建集合:
db.createCollection("validated_movies",{validator:{$jsonSchema:{bsonType:"object",required:["title","year","runtime"],properties:{title:{bsonType:"string",description:"must be a string and is required"},year:{bsonType:"int",minimum:1888,description:"must be an integer >= 1888 (year of the first film)"},runtime:{bsonType:"int",minimum:1,description:"must be a positive integer and is required"},genres:{bsonType:"array",items:{bsonType:"string"},description:"must be an array of strings"}}}},validationLevel:"strict",validationAction:"error"});技术支持
db.runCommand({collMod:"movies",validator:{$jsonSchema:{bsonType:"object",required:["title"],properties:{title:{bsonType:"string",description:"must be a string and is required"}}}},validationLevel:"moderate",validationAction:"warn"});技术支持
图 3. 由于验证动作设置为 "warn",insertOne() 命令成功添加了一条违反架构规则的文档。MongoDB 接受了写入,但若强制 "error",缺失的 title 字段将触发验证错误。
验证嵌套文档与数组
MongoDB 文档经常包含嵌套结构,如对象中套对象、值的数组。所幸,$jsonSchema 支持对两者进行验证。让我们使用 movies 集合中的字段,演示如何对子文档与数组内容应用规则。
验证嵌套文档
在 movies 集合中,每条文档都包含一个嵌入的 imdb 对象,其中包含 rating、votes 与 id 等字段。我们来强制以下规则:
imdb 是一个对象(若存在)。
imdb.rating 是 0 到 10 之间的数字。
定义如下:
db.createCollection("validated_nested",{validator:{$jsonSchema:{bsonType:"object",properties:{imdb:{bsonType:"object",properties:{rating:{bsonType:"double",minimum:0,maximum:10,description:"Must be a number between 0 and 10"}}}}}}});技术支持
db.createCollection("validated_full",{validator:{$jsonSchema:{bsonType:"object",required:["title","year"],properties:{title:{bsonType:"string",description:"Title is required and must be a string"},year:{bsonType:"int",minimum:1888,description:"Year must be an integer >= 1888"},rated:{enum:["G","PG","PG-13","R","NC-17"],description:"Must be a valid MPAA rating"},genres:{bsonType:"array",minItems:1,items:{bsonType:"string"},description:"Must be a non-empty array of strings"},imdb:{bsonType:"object",properties:{rating:{bsonType:"double",minimum:0,maximum:10,description:"IMDB rating must be between 0 and 10"}}},tomatoes:{bsonType:"object",properties:{viewer:{bsonType:"object",properties:{rating:{bsonType:"double",minimum:0,maximum:5,description:"Viewer rating must be between 0 and 5"}}}}}}}}});技术支持
图 7. 该查询在 movies 集合中结合使用 $nor 与 $jsonSchema,查找没有类型为字符串的 title 字段的文档。结果显示了一条仅包含 year 字段、_id 为 683ce0050bd195c35e417534 的文档,确认其违反了要求 title 的架构。此技巧可用于在实施更严格验证规则前识别无效文档。
MongoDB 也许是无架构的,但这并不意味着您的数据必须杂乱无章。
在本文中,您将从架构验证的基础到使用
$jsonSchema运算符的高级用例,全面了解相关内容。您将学习如何强制必填字段、限制数据类型、应用模式,以及验证嵌套文档。我们还将介绍验证在插入与更新期间如何工作、如何修改现有集合,以及何时使用不同的验证级别与动作。
最后,您将能够自信地将架构验证应用到 MongoDB 集合中,在不牺牲 MongoDB 著名灵活性的前提下,保持数据干净且可预测。您也可以查看我们的架构验证视频以进一步了解。
为什么架构验证在 MongoDB 中很重要
MongoDB 的动态文档模型允许同一集合中的文档拥有不同的字段或结构,从而提供了速度与灵活性。这在早期开发或快速迭代期间优势巨大。然而,随着应用的增长,这种灵活性也可能迅速演变为负担,导致数据不一致、不完整或无效。
架构验证 正是为此而生。它允许您为文档定义规则——例如必填字段、特定数据类型或嵌套结构——同时仍然拥抱 MongoDB 的灵活本质。
在本文中,您将学习如何使用 MongoDB 强大的
$jsonSchema运算符将这些规则应用到您的集合中。无论您是在新的应用中防止错误插入,还是在遗留数据库上补充验证规则,架构验证都能帮助确保数据保持清洁、查询保持可预测、应用保持稳定。如果您刚开始使用 MongoDB 与 Python,这门MongoDB 入门课程是您在深入架构验证之前,上手查询与集成的绝佳方式。
开始之前
为充分利用本指南,您需要访问一个 MongoDB Atlas 集群。免费层足以跟随学习。
为尽可能清晰、可用地阐释概念,我们将使用一个MongoDB Atlas 示例数据集——具体来说是
sample_mflix数据库。该数据集包含 movies、comments、users 与 theaters 等集合。我们将在全文中引用这些集合演示每个示例。数据集中的文档包含嵌套对象、数组、字符串、数字以及可选字段等。加载示例数据集(如需)
1. 前往您的 MongoDB Atlas 控制台。
2. 选择您的集群,点击“…”按钮,并选择 Load Sample Dataset。
3. 等待几分钟,直到 sample_mflix 数据库可用。
从架构验证入门
数据集加载完成后,使用 MongoDB Shell(mongosh)或 MongoDB Compass 连接到您的集群,然后切换到该数据集:
使用 findOne() 探索数据集
让我们检查 movies 集合中的一条文档:
以下是您可能看到的简化版本:
图 1. 该查询使用 findOne() 从 sample_mflix 数据库的 movies 集合中检索一条示例文档。结果显示多个字段,包括 title、year、genres,以及像 imdb 和 tomatoes.viewer 这样的嵌套对象。
此文档包含标量值、数组与嵌套对象的混合,十分适合展示各种验证技术。在下一节中,我们将基于此结构创建一个新集合,并使用
$jsonSchema定义第一个验证架构。受支持的关键字、扩展与限制
MongoDB 的
$jsonSchema验证基于 JSON Schema Draft 4 规范,并做了一些修改与取舍。尽管原规范的所有特性并未全部支持,MongoDB 提供了一个聚焦且实用的子集,足以验证 BSON 文档。以下是最常用的一些关键字:
MongoDB 有意省略了完整 JSON Schema 中的一些高级特性,例如:
完整的受支持选项列表请查阅 MongoDB 官方 $jsonSchema 文档。
本文将聚焦最实用、最广泛支持的关键字,足以覆盖常见的真实世界验证需求,而不至于过于复杂。
若想更广泛地了解 MongoDB 在 NoSQL 领域中的定位,请阅读这篇综合教程。
创建带有基础验证的集合
既然我们已经了解了 movies 文档的结构,就使用
$jsonSchema创建一个新集合来强制一些基本规则。我们将从几个常见约束开始:我们将该新集合命名为
validated_movies。创建集合
运行以下命令以带验证地创建集合:
插入有效文档
现在,插入一条符合架构的文档:
✅ 这将成功。
插入无效文档
尝试插入缺少
title字段的文档:❌ 这将因验证错误而失败,因为
title为必填。图 2. 第一个命令将一条有效的电影文档插入 validated_movies 集合,满足所有必需字段和数据类型。第二个命令尝试插入缺少必填字段 title 的文档,因此触发验证错误。
处理现有集合
使用架构验证并不需要从零开始。MongoDB 允许您使用
collMod命令将验证规则应用到现有集合。这在您希望为遗留数据集增加结构、且不影响现有文档时尤其有用。本节中,我们将为
sample_mflix数据库中的 movies 集合应用一个验证器。使用 collMod 添加验证器
让我们强制一条基础规则:每个电影文档都必须具有类型为字符串的
title字段。检查集合上的验证规则
使用
collMod修改movies集合后,您可能希望确认当前生效的验证规则。要检查当前的架构验证器,请运行:
这将返回定义集合验证规则的
$jsonSchema对象。它是验证您的配置是否正确应用的实用方式,尤其是在从 warn 切换到 error,或更新必填字段结构之后。理解 validationLevel 与 validationAction
尝试插入一条违反架构的文档:
您将在日志中看到一条警告,但写入会成功。稍后当您有把握时,可以将动作改为 "error" 来严格执行规则。
图 3. 由于验证动作设置为 "warn",insertOne() 命令成功添加了一条违反架构规则的文档。MongoDB 接受了写入,但若强制 "error",缺失的 title 字段将触发验证错误。
验证嵌套文档与数组
MongoDB 文档经常包含嵌套结构,如对象中套对象、值的数组。所幸,
$jsonSchema支持对两者进行验证。让我们使用 movies 集合中的字段,演示如何对子文档与数组内容应用规则。验证嵌套文档
在 movies 集合中,每条文档都包含一个嵌入的
imdb对象,其中包含rating、votes与id等字段。我们来强制以下规则:imdb是一个对象(若存在)。imdb.rating是 0 到 10 之间的数字。定义如下:
测试嵌套文档
✅ 有效插入:
❌ 无效插入(rating 超出范围):
这将引发验证错误,因为 rating 大于 10。
图 4. 该插入操作失败,因为嵌套 imdb 对象内的 rating 字段超过了架构定义的最大值 10。由于违反了 $jsonSchema 规则,MongoDB 抛出验证错误。
验证数组
您也可以对数组施加约束。比如,您希望确保:
genres是字符串数组。将以下内容加入您的架构:
组合多重约束
在真实应用中,您的文档常常需要同时满足多条验证规则——涵盖必填字段、允许值、类型以及嵌套结构。借助
$jsonSchema,您可以轻松在单个架构定义中组合多重约束。让我们基于 movies 文档结构构建一个更完整的示例。将强制以下规则:
创建带完整验证的集合
插入有效文档:
插入无效文档:
将因以下原因失败:
rated值无效。genres数组为空。imdb.rating超过 10。tomatoes.viewer.rating超过 5。图 5. 该插入操作因 validated_full 集合中的多项架构违规而失败。rated 字段包含不在枚举中的值,genres 是违反 minItems 规则的空数组,imdb.rating 超过最大值 10 且类型错误,tomatoes.viewer.rating 超过允许的最大值 5。MongoDB 会以详细的验证错误拒绝插入。
验证级别与动作详解
MongoDB 提供了可精细控制的方式来决定何时以及如何应用架构验证。在定义验证规则时,有两个选项会影响数据库行为:
validationLevel
此选项控制哪些操作会受到架构验证的约束。
使用建议:
validationAction
此选项控制当文档违反验证规则时,MongoDB 的响应方式。
重要提示:在 MongoDB Atlas 上,来自 warn 的警告对用户不可见——您不会在 UI 或标准日志中看到它们。
使用建议:
示例:从 Warn 切换到 Error
假设您一开始是这样的:
在观察应用行为后,您可以切换到严格执行:
图 6. 使用两个 collMod 命令修改 validated_full 集合的验证行为。第一个将 validationAction 设为 "warn"、validationLevel 设为 "moderate",允许插入无效文档同时记录内部警告。第二个命令将 validationAction 设为 "error"、validationLevel 设为 "strict",切换为严格执行,确保所有插入与更新都完全遵循定义的架构。
现在,任何无效的插入或更新都会被错误信息阻止。
常见陷阱与调试技巧
在使用架构验证时,小错误可能导致令人困惑的报错——更糟的是,文档可能悄然绕过您的规则。以下是一些最常见的问题及其规避方法:
错误的 bsonType 值
使用
$jsonSchema时最常见的错误之一是混淆 JSON 类型与 MongoDB 的“bsonType”。虽然 "string" 与 "array" 直观明了,但数字与布尔值等可能会比较棘手。例如:
如果不确定字段存储的类型,您可以在 Mongo shell 中使用 typeof 检查文档,或在 MongoDB Compass 中查看数据。
部分更新中缺少必填字段
当使用
$set时,验证只检查被修改的字段。如果您期望进行完整架构检查,请确保执行整文档替换,或结合使用validationLevel: "strict"与应用逻辑来强制完整结构。本应不通过却通过的空数组
若忘记指定
minItems,空数组会通过验证:使用
minItems: 1以确保至少存在一个值。warn 模式下没有错误
如前所述,
validationAction: "warn"不会阻止写入,且在 MongoDB Atlas 中不会显示错误。它只会进行内部日志记录,因此验证看起来似乎“没有生效”。在开发或测试环境中使用 "error" 模式,以便及早捕获问题。
父对象缺失时,不会检查嵌套字段路径
如果要求
imdb.rating必填,但imdb整体缺失,MongoDB 不会检查rating。要对嵌套字段施加验证,请确保其父对象要么为必填,要么具有
bsonType: "object"。查询或更新不匹配架构的文档
将验证应用到现有集合后,识别不符合新架构的文档通常很有帮助。您可以在查询中将
$jsonSchema与$nor或$not结合使用。示例:查找 movies 集合中的无效文档
假设您的架构要求 title 的类型为 "string"。要查找不匹配的文档,您可以运行:
这将返回缺少 title 或 title 类型错误的文档。
图 7. 该查询在 movies 集合中结合使用 $nor 与 $jsonSchema,查找没有类型为字符串的 title 字段的文档。结果显示了一条仅包含 year 字段、_id 为 683ce0050bd195c35e417534 的文档,确认其违反了要求 title 的架构。此技巧可用于在实施更严格验证规则前识别无效文档。
您还可以将这种方法用于:
总结:在不牺牲灵活性的前提下获得干净数据
MongoDB 的无架构设计是其最大优势之一,但这并不意味着您应该放任数据不受约束。借助
$jsonSchema,您可以定义强大的验证规则,在利用 MongoDB 灵活性的同时强制结构化。通过本文,您已经学会:
无论是构建全新应用,还是为遗留系统增加结构,架构验证都能在不拖慢开发的情况下为数据提供保护。
后续步骤与资源
想要更深入?请查看以下资源:
在准备面试?这些顶级 MongoDB 题目将检验您对架构验证等主题的理解。想要验证您的技能?这份认证指南概述了关于 MongoDB 认证的一切要点。
请记住,从小处着手,先用 "warn" 测试,待有把握后再切换到 "error"——未来的您(以及您的数据)都会感谢现在的选择。