跳到主要内容
版本:5.1.1

JSON 模式

Doubao 中英对照 JSON Schema

从 3.6 版起,MongoDB 支持根据提供的 JSON 模式 来校验文档的集合。如以下示例所示,模式本身以及校验动作和级别都可以在创建集合时定义:

示例 1. JSON 模式示例

{
"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 }
}
}
}
}
  • JSON 模式文档始终从根开始描述整个文档。模式本身是一个模式对象,可以包含描述属性和子文档的内嵌模式对象。

  • required 是一个描述文档中哪些属性为必需的属性。它可以与其他模式约束一起可选地指定。请参阅 MongoDB 关于可用关键词的文档。

  • properties 与描述 object 类型的模式对象相关。它包含属性特有的模式约束。

  • firstname 为文档内的 firstname 字段指定约束。此处它是一个基于字符串的 properties 元素,声明了可能的字段值。

  • address 是一个子文档,为其 postCode 字段的值定义模式。

您可以通过指定模式文档(即使用 Document API 解析或构建文档对象),或使用 org.springframework.data.mongodb.core.schema 中 Spring Data 的 JSON 模式工具来构建它。MongoJsonSchema 是所有 JSON 模式相关操作的入口。以下示例展示了如何使用 MongoJsonSchema.builder() 创建 JSON 模式:

示例 2. 创建 JSON 模式

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
  • 获取一个模式构建器,以流式 API 配置模式。

  • 配置必需属性,既可如此处直接配置,也可如第 3 点那样提供更多细节。

  • 配置必需的 String 类型 firstname 字段,仅允许 luke 和 han 值。属性可以是带类型的或不带类型的。静态导入 JsonSchemaProperty 可使语法更简洁,并提供 string(…) 等入口。

  • 构建模式对象。

网关接口的静态方法已提供一些预定义的强类型模式对象(JsonSchemaObject 和 JsonSchemaProperty)。不过,您可能需要构建自定义属性校验规则,这些规则可以通过构建器 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 提供了集合模式支持的入口,如下例所示:

示例 3. 使用 $jsonSchema 创建集合

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

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

生成模式​

设置模式可能是一项耗时的工作,我们鼓励所有决定这样做的人真正投入所需的时间。这很重要,因为模式变更可能很困难。不过,有时您可能不想为此烦恼,这时 JsonSchemaCreator 就派上用场了。

JsonSchemaCreator 及其默认实现会根据映射基础设施提供的领域类型元数据生成 MongoJsonSchema。这意味着会同时考虑注解属性以及可能的自定义转换。

示例 4. 从领域类型生成 JSON 模式

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;
}

// getter / 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
}
}
  • 简单对象属性被视为常规属性。

  • 原始类型被视为必需属性。

  • 枚举被限制为可能的取值。

  • 对象类型属性会被检查并表示为嵌套文档。

  • 由转换器转换为 Code 的 String 类型属性。

  • 生成模式时会忽略 @Transient 属性。

备注

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

表 1. 特殊模式生成规则

Java模式类型说明
Objecttype : object若有可用元数据,则带 properties。
Collectiontype : array-
Maptype : object-
Enumtype : string带 enum 属性,持有可能的枚举值。
arraytype : array简单类型数组,除非是 byte[]。
byte[]bsonType : binData-

上面的示例展示了如何从类型非常精确的源派生模式。在领域模型中使用多态元素可能导致 Object 和泛型 <T> 类型的模式表示不准确,它们很可能在没有进一步说明的情况下被表示为 { type : 'object' }。MongoJsonSchemaCreator.property(…) 允许定义额外细节,例如渲染模式时应考虑的嵌套文档类型。

示例 5. 为属性指定额外类型

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' }
}
}
}
}
  • 给定类型的属性被合并到一个元素中。

MongoDB 无模式的方式允许在一个集合中存储结构不同的文档。这些文档可能建模为具有共同基类。无论选择哪种方式,MongoJsonSchemaCreator.merge(…) 都可以帮助免去将多个模式合并为一个的麻烦。

示例 6. 将多个模式合并为单个模式定义

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' }
}
}
}
  • 给定类型的属性(及其继承的属性)被组合到一个模式中。

备注

同名属性必须指向相同的 JSON 模式才能被组合。以下示例展示了一个因数据类型不匹配而无法自动合并的定义。在这种情况下,必须向 MongoJsonSchemaCreator 提供 ConflictResolutionFunction。

class A extends Root {
String value;
}

class B extends Root {
Integer value;
}

加密字段​

MongoDB 4.2 的字段级加密允许直接加密单个属性。

在设置 JSON 模式时,可以将属性包裹在加密属性中,如下例所示。

示例 7. 通过 JSON 模式实现客户端字段级加密

MongoJsonSchema schema = MongoJsonSchema.builder()
.properties(
encrypted(string("ssn"))
.algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")
.keyId("*key0_id")
).build();

除了手动定义加密字段外,还可以利用 @Encrypted 注解,如下例代码片段所示。

示例 8. 通过 JSON 模式实现客户端字段级加密

@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;
}
  • 将为 encryptMetadata 设置的默认加密设置。

  • 使用默认加密设置的加密字段。

  • 覆盖默认加密算法的加密字段。

提示

@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 模式类型​

下表列出了支持的 JSON 模式类型:

表 2. 支持的 JSON 模式类型

模式类型Java 类型模式属性
untyped-description、生成的 description、enum、allOf、anyOf、oneOf、not
objectObjectrequired、additionalProperties、properties、minProperties、maxProperties、patternProperties
arraybyte[] 之外的任何数组uniqueItems、additionalItems、items、minItems、maxItems
stringStringminLength、maxLentgth、pattern
intint、IntegermultipleOf、minimum、exclusiveMinimum、maximum、exclusiveMaximum
longlong、LongmultipleOf、minimum、exclusiveMinimum、maximum、exclusiveMaximum
doublefloat、Float、double、DoublemultipleOf、minimum、exclusiveMinimum、maximum、exclusiveMaximum
decimalBigDecimalmultipleOf、minimum、exclusiveMinimum、maximum、exclusiveMaximum
numberNumbermultipleOf、minimum、exclusiveMinimum、maximum、exclusiveMaximum
binDatabyte[](无)
booleanboolean、Boolean(无)
nullnull(无)
objectIdObjectId(无)
datejava.util.Date(无)
timestampBsonTimestamp(无)
regexjava.util.regex.Pattern(无)
备注

untyped 是一个泛型类型,所有带类型的模式类型都继承自它。它为带类型的模式类型提供所有 untyped 模式属性。

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