4.3. Schema 校验

指定校验规则

如果创建新 collection 时要指定校验规则,需要在使用 db.createCollection() (opens new window)时指定 validator 选项。如果要将 document 校验添加到现有 collection 中,需要使用带有 validator 选项的 collMod (opens new window)命令。MongoDB 还提供以下相关选项:

  • validationLevel 选项(用于确定 MongoDB 在更新过程中,对现有 document 应用校验规则的严格程度)
  • validationAction 选项(用于确定 MongoDB 发现违反校验规则的 document 时,是选择报错并拒绝,还是接受数据但在日志中告警)。

JSON Schema

从 3.6 版本开始,MongoDB 开始支持 JSON Schema 校验。可以通过在 validator 表达式中使用 $jsonSchema (opens new window)操作来指定 JSON Schema 校验。

db.createCollection('students', {
  validator: {
    $jsonSchema: {
      bsonType: 'object',
      required: ['name', 'year', 'major', 'address'],
      properties: {
        name: {
          bsonType: 'string',
          description: 'must be a string and is required',
        },
        year: {
          bsonType: 'int',
          minimum: 2017,
          maximum: 3017,
          description: 'must be an integer in [ 2017, 3017 ] and is required',
        },
        major: {
          enum: ['Math', 'English', 'Computer Science', 'History', null],
          description: 'can only be one of the enum values and is required',
        },
        gpa: {
          bsonType: ['double'],
          description: 'must be a double if the field exists',
        },
        address: {
          bsonType: 'object',
          required: ['city'],
          properties: {
            street: {
              bsonType: 'string',
              description: 'must be a string if the field exists',
            },
            city: {
              bsonType: 'string',
              description: 'must be a string and is required',
            },
          },
        },
      },
    },
  },
})

其它查询表达式

除了使用 $jsonSchema (opens new window)查询运算符的 JSON Schema 校验外,MongoDB 还支持其它查询运算符的校验,但以下情况除外:

【示例】查询表达式中指定校验规则

db.createCollection('contacts', {
  validator: {
    $or: [
      { phone: { $type: 'string' } },
      { email: { $regex: /@mongodb\.com$/ } },
      { status: { $in: ['Unknown', 'Incomplete'] } },
    ],
  },
})

行为

校验发生在更新和插入期间。添加校验规则到 collection 时,不会对现有的 document 进行校验,除非发生修改操作。

现有的 document

validationLevel 选项确定 MongoDB 进行规则校验时执行的操作:

  • 如果 validationLevel 是 strict(严格级别。这是 MongoDB 默认级别),则 MongoDB 将校验规则应用于所有插入和更新。
  • 如果 validationLevel 是 moderate(中等级别),则 MongoDB 只对已满足校验条件的现有文档的插入和更新操作进行校验;对不符合校验标准的现有文档的更新操作不进行校验。

下面是一个正常的插入操作:

db.contacts.insert([
  {
    _id: 1,
    name: 'Anne',
    phone: '+1 555 123 456',
    city: 'London',
    status: 'Complete',
  },
  { _id: 2, name: 'Ivan', city: 'Vancouver' },
])

在 collection 上配置一个校验规则:

db.runCommand({
  collMod: 'contacts',
  validator: {
    $jsonSchema: {
      bsonType: 'object',
      required: ['phone', 'name'],
      properties: {
        phone: {
          bsonType: 'string',
          description: 'must be a string and is required',
        },
        name: {
          bsonType: 'string',
          description: 'must be a string and is required',
        },
      },
    },
  },
  validationLevel: 'moderate',
})

contacts collection 现在添加了含中等级别(moderate) validationLevel 的 validator

  • 如果尝试更新 _id为 1 的文档,则 MongoDB 将应用校验规则,因为现有文档符合条件。
  • 相反,MongoDB 不会将校验 _id 为 2 的文档,因为它不符合校验规则。

如果要完全禁用校验,可以将 validationLevel 置为 off

接受或拒绝无效的 document

  • 如果 validationAction 是 Error(默认),则 MongoDB 拒绝任何违反校验规则的插入或更新。
  • 如果 validationAction 是 Warn,MongoDB 会记录所有的违规,但允许进行插入或更新。

创建集合时,配置 validationAction 为 warn。

db.createCollection('contacts2', {
  validator: {
    $jsonSchema: {
      bsonType: 'object',
      required: ['phone'],
      properties: {
        phone: {
          bsonType: 'string',
          description: 'must be a string and is required',
        },
        email: {
          bsonType: 'string',
          pattern: '@mongodb.com$',
          description:
            'must be a string and match the regular expression pattern',
        },
        status: {
          enum: ['Unknown', 'Incomplete'],
          description: 'can only be one of the enum values',
        },
      },
    },
  },
  validationAction: 'warn',
})

尝试插入一条违规记录

> db.contacts2.insert( { name: "Amanda", status: "Updated" } )
WriteResult({ "nInserted" : 1 })

MongoDB 允许这条操作执行,但是服务器会记录下告警信息。

{"t":{"$date":"2020-09-11T16:35:57.754+08:00"},"s":"W",  "c":"STORAGE",  "id":20294,   "ctx":"conn14","msg":"Document would fail validation","attr":{"namespace":"test.contacts2","document":{"_id":{"$oid":"5f5b36ed8ea53d62a0b51c4e"},"name":"Amanda","status":"Updated"}}}

限制

不能在 adminlocalconfig 这几个特殊的数据库中指定校验规则。不能在 system.* collection 中指定校验。