JSON Schema

从版本 3.6 开始,MongoDB 支持根据提供的 JSON Schema验证文档的集合。创建集合时可以定义架构本身以及验证操作和级别,如下例所示: .Sample JSON schema

{
  "type": "object",                                                        1

  "required": [ "firstname", "lastname" ],                                 2

  "properties": {                                                          3

    "firstname": {                                                         4
      "type": "string",
      "enum": [ "luke", "han" ]
    },
    "address": {                                                           5
      "type": "object",
      "properties": {
        "postCode": { "type": "string", "minLength": 4, "maxLength": 5 }
      }
    }
  }
}
1 JSON 模式文档总是从其根部描述一个完整的文档。模式本身是一个可以包含嵌入模式对象的模式对象,该模式对象描述属性和子文档。
2 required 是一个属性,描述了文档中需要哪些属性。它可以与其他架构约束一起选择性地指定。请参阅 MongoDB 关于 available keywords的文档。
3 properties 与一个描述 object 类型的模式对象相关。它包含特定于属性的模式约束。
4 firstname 指定文档中 firstname 字段的约束。在此,它是一个基于字符串的 properties 元素,声明可能的字段值。
5 address 是一个子文档,它为其 postCode 字段中的值定义一个模式。

您可以通过指定模式文档(即使用 Document API 解析或构建文档对象)或使用 Spring Data 的 org.springframework.data.mongodb.core.schema 中的 JSON 模式实用程序来构建模式来提供此模式。MongoJsonSchema 是所有 JSON 模式相关操作的入口点。以下示例显示了如何使用 MongoJsonSchema.builder() 创建 JSON 模式: .Creating a JSON schema

MongoJsonSchema.builder()                                                    1
    .required("lastname")                                                    2

    .properties(
                required(string("firstname").possibleValues("luke", "han")), 3

                object("address")
                     .properties(string("postCode").minLength(4).maxLength(5)))

    .build();                                                                4
1 获取一个模式生成器,以使用流畅的 API 配置模式。
2 直接配置必需的属性,如此处所示,或像在 3 中那样提供更多详细信息。
3 配置必需的 String 类型 firstname 字段,仅允许 lukehan 值。属性可以是类型化的或非类型化的。使用 JsonSchemaProperty 的静态导入来使语法变得更简洁,并获取像 string(…) 这样的入口点。
4 Build the schema object.

网关接口上的静态方法中已经提供了一些预定义且强类型的模式对象(JsonSchemaObjectJsonSchemaProperty)。但是,您可能需要构建自定义属性验证规则,可以通过构建器 API 创建这些规则,如下例所示:

// "birthdate" : { "bsonType": "date" }
JsonSchemaProperty.named("birthdate").ofType(Type.dateType());

// "birthdate" : { "bsonType": "date", "description", "Must be a date" }
JsonSchemaProperty.named("birthdate").with(JsonSchemaObject.of(Type.dateType()).description("Must be a date"));

CollectionOptions 为集合的模式支持提供了入口点,如下例所示: .Create collection with $jsonSchema

MongoJsonSchema schema = MongoJsonSchema.builder().required("firstname", "lastname").build();

template.createCollection(Person.class, CollectionOptions.empty().schema(schema));

Generating a Schema

设置模式可能是一项耗时的任务,我们鼓励每个人在决定这样做时真正花时间去完成。请务必注意,模式更改可能很困难。不过,有时人们可能不想因此而退缩,这就是 JsonSchemaCreator 发挥作用的地方。

JsonSchemaCreator`及其默认实现是从映射基础结构提供的域类型元数据中生成 `MongoJsonSchema。这意味着,会考虑 annotated properties以及潜在的 custom conversions

Example 1. Generate Json Schema from domain type
public class Person {

    private final String firstname;                   1
    private final int age;                            2
    private Species species;                          3
    private Address address;                          4
    private @Field(fieldType=SCRIPT) String theForce; 5
    private @Transient Boolean useTheForce;           6

    public Person(String firstname, int age) {        1 2

        this.firstname = firstname;
        this.age = age;
    }

    // gettter / setter omitted
}

MongoJsonSchema schema = MongoJsonSchemaCreator.create(mongoOperations.getConverter())
    .createSchemaFor(Person.class);

template.createCollection(Person.class, CollectionOptions.empty().schema(schema));
{
    'type' : 'object',
    'required' : ['age'],                     2
    'properties' : {
        'firstname' : { 'type' : 'string' },  1
        'age' : { 'bsonType' : 'int' }        2
        'species' : {                         3
            'type' : 'string',
            'enum' : ['HUMAN', 'WOOKIE', 'UNKNOWN']
        }
        'address' : {                         4
            'type' : 'object'
            'properties' : {
                'postCode' : { 'type': 'string' }
            }
        },
        'theForce' : { 'type' : 'javascript'} 5
     }
}
1 简单的对象属性被视为常规属性。
2 基本类型被视为必需的属性。
3 枚举被限制到可能的值。
4 对象类型属性将被检查并表示为嵌套文档。
5 被转换器转换为 CodeString 类型属性。
6 生成架构时,将忽略 @Transient 属性。

_id 使用可转换为 ObjectId(例如 String)的类型的属性会映射到 { type : 'object' },除非通过 @MongoId 注释提供更具体的信息。

Table 1. Sepcial Schema Generation rules
Java Schema Type Notes

Object

type : object

如果元数据可用,则使用 properties

Collection

type : array

-

Map

type : object

-

Enum

type : string

具有保存可能的枚举值的属性的 enum

array

type : array

简单类型数组,除非是 byte[]

byte[]

bsonType : binData

-

上面的示例演示了如何从非常精确的类型化来源派生模式。在域模型中使用多态元素可能会导致 Object 和泛型 <T> 类型的模式表示不准确,这些类型可能表示为 { type : 'object' },而没有进一步的说明。MongoJsonSchemaCreator.property(…) 允许定义其他详细信息,例如应在呈现模式时考虑的嵌套文档类型。

Example 2. Specify additional types for properties
class Root {
	Object value;
}

class A {
	String aValue;
}

class B {
	String bValue;
}
MongoJsonSchemaCreator.create()
    .property("value").withTypes(A.class, B.class) 1
{
    'type' : 'object',
    'properties' : {
        'value' : {
            'type' : 'object',
            'properties' : {                       1
                'aValue' : { 'type' : 'string' },
                'bValue' : { 'type' : 'string' }
            }
        }
    }
}
1 给定类型的属性将合并到一个元素中。

MongoDB 的无模式方法允许在一个集合中存储不同结构的文档。这些可能被建模为具有公共基类。无论选择何种方式,MongoJsonSchemaCreator.merge(…) 都有助于避免将多个模式合并为一个模式的需求。

Example 3. Merging multiple Schemas into a single Schema definition
abstract class Root {
	String rootValue;
}

class A extends Root {
	String aValue;
}

class B extends Root {
	String bValue;
}

MongoJsonSchemaCreator.mergedSchemaFor(A.class, B.class) 1
{
    'type' : 'object',
       'properties' : { 1
           'rootValue' : { 'type' : 'string' },
           'aValue' : { 'type' : 'string' },
           'bValue' : { 'type' : 'string' }
       }
    }
}
1 给定类型的属性(及其继承的属性)将合并到一个架构中。

对于要合并的同名属性,需要引用同一 JSON 架构。以下示例显示一个定义,该定义无法自动合并,因为数据类型不匹配。在这种情况下,必须向 MongoJsonSchemaCreator 提供 ConflictResolutionFunction

class A extends Root {
	String value;
}

class B extends Root {
	Integer value;
}

Encrypted Fields

MongoDB 4.2 Field Level Encryption允许直接加密各个属性。

在设置 JSON 架构时,可以在加密属性中包装属性,如下面的示例所示。

Example 4. Client-Side Field Level Encryption via Json Schema
MongoJsonSchema schema = MongoJsonSchema.builder()
    .properties(
        encrypted(string("ssn"))
            .algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")
            .keyId("*key0_id")
	).build();

您可以利用 @Encrypted 注解(如下面的代码段所示)来替换手动定义已加密字段。

Example 5. Client-Side Field Level Encryption via Json Schema
@Document
@Encrypted(keyId = "xKVup8B1Q+CkHaVRx+qa+g==", algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random") 1
static class Patient {

    @Id String id;
    String name;

    @Encrypted 2
    String bloodType;

    @Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic") 3
    Integer ssn;
}
1 将为 encryptMetadata 设置的默认加密设置。
2 使用默认加密设置的加密字段。
3 使用默认加密算法重写加密字段。

@Encrypted 注解支持通过 SpEL 表达式解析 keyId。要执行此操作需要额外的环境元数据(通过 MappingContext),并且必须提供该元数据。

@Document
@Encrypted(keyId = "#{mongocrypt.keyId(#target)}")
static class Patient {

    @Id String id;
    String name;

    @Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Random")
    String bloodType;

    @Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")
    Integer ssn;
}

MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
MongoJsonSchema patientSchema = schemaCreator
    .filter(MongoJsonSchemaCreator.encryptedOnly())
    .createSchemaFor(Patient.class);

mongocrypt.keyId 函数通过 EvaluationContextExtension 定义,如下面的代码段所示。提供自定义扩展是计算 keyId 的最灵活方式。

public class EncryptionExtension implements EvaluationContextExtension {

    @Override
    public String getExtensionId() {
        return "mongocrypt";
    }

    @Override
    public Map<String, Function> getFunctions() {
        return Collections.singletonMap("keyId", new Function(getMethod("computeKeyId", String.class), this));
    }

    public String computeKeyId(String target) {
        // ... lookup via target element name
    }
}

JSON Schema Types

下表显示了受支持的 JSON 架构类型:

Table 2. Supported JSON schema types
Schema Type Java Type Schema Properties

untyped

-

description、生成的 descriptionenumallOfanyOfoneOfnot

object

Object

requiredadditionalPropertiespropertiesminPropertiesmaxPropertiespatternProperties

array

any array except byte[]

uniqueItemsadditionalItemsitemsminItemsmaxItems

string

String

minLength, maxLentgth, pattern

int

int, Integer

multipleOfminimumexclusiveMinimummaximumexclusiveMaximum

long

long, Long

multipleOfminimumexclusiveMinimummaximumexclusiveMaximum

double

float, Float, double, Double

multipleOfminimumexclusiveMinimummaximumexclusiveMaximum

decimal

BigDecimal

multipleOfminimumexclusiveMinimummaximumexclusiveMaximum

number

Number

multipleOfminimumexclusiveMinimummaximumexclusiveMaximum

binData

byte[]

(none)

boolean

boolean, Boolean

(none)

null

null

(none)

objectId

ObjectId

(none)

date

java.util.Date

(none)

timestamp

BsonTimestamp

(none)

regex

java.util.regex.Pattern

(none)

untyped 是由所有类型化模式类型继承的泛型类型。它为所有 untyped 模式属性提供类型化模式类型。

有关更多信息,请参阅 $jsonSchema