跳到主要内容

JSON Schema

DeepSeek V3 中英对照 JSON Schema

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

示例 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
  • JSON 模式文档总是从根节点描述整个文档。模式本身是一个模式对象,可以包含描述属性和子文档的嵌入式模式对象。

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

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

  • firstname 指定了文档中 firstname 字段的约束。在这里,它是一个基于字符串的 properties 元素,声明了可能的字段值。

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

你可以通过指定一个模式文档(即使用 Document API 来解析或构建一个文档对象)或通过使用 Spring Data 的 JSON 模式工具在 org.springframework.data.mongodb.core.schema 中构建模式来提供模式。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
java
  • 获取一个模式构建器,使用流畅的 API 来配置模式。

  • 直接配置所需的属性,如这里所示,或者如第 3 点所示进行更详细的配置。

  • 配置所需的 String 类型 firstname 字段,仅允许 lukehan 值。属性可以是类型化的或非类型化的。使用 JsonSchemaProperty 的静态导入可以使语法稍微更紧凑,并获得诸如 string(…) 等入口点。

  • 构建模式对象。

通过网关接口上的静态方法,已经有一些预定义且强类型的模式对象(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"));
java

CollectionOptions 提供了集合的 schema 支持的入口点,如下例所示:

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

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

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

生成模式

设置 schema 可能是一项耗时的任务,我们鼓励所有决定这样做的人,真正花时间去做。这很重要,因为 schema 的更改可能会很困难。然而,有时候人们可能不想被它困扰,这时 JsonSchemaCreator 就派上了用场。

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

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

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));
java
{
'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
}
}
json
  • 简单对象属性被视为常规属性。

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

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

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

  • String 类型属性通过转换器转换为 Code

  • @Transient 属性在生成模式时被省略。

备注

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

表 1. 特殊模式生成规则

JavaSchema 类型备注
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
java
{
'type' : 'object',
'properties' : {
'value' : {
'type' : 'object',
'properties' : { 1
'aValue' : { 'type' : 'string' },
'bValue' : { 'type' : 'string' }
}
}
}
}
json
  • 给定类型的属性将合并到一个元素中。

MongoDB 的无模式方法允许在一个集合中存储不同结构的文档。这些文档可以使用一个共同的基类进行建模。无论选择哪种方法,MongoJsonSchemaCreator.merge(…) 都可以帮助避免将多个模式合并为一个的需求。

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

abstract class Root {
String rootValue;
}

class A extends Root {
String aValue;
}

class B extends Root {
String bValue;
}

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

备注

具有相同名称的属性需要引用相同的 JSON 模式才能进行合并。以下示例展示了一个由于数据类型不匹配而无法自动合并的定义。在这种情况下,必须为 MongoJsonSchemaCreator 提供一个 ConflictResolutionFunction

class A extends Root {
String value;
}

class B extends Root {
Integer value;
}
java

加密字段

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

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

示例 7:通过 Json Schema 实现的客户端字段级加密

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

相比于手动定义加密字段,可以使用 @Encrypted 注解来实现,如下面的代码片段所示。

示例 8. 通过 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;
}
java
  • 将设置为 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);
java

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) {
// ... 通过目标元素名称查找
}
}
java

JSON Schema 类型

下表展示了支持的 JSON schema 类型:

表 2. 支持的 JSON schema 类型

Schema 类型Java 类型Schema 属性
untyped-description, 自动生成的 description, enum, allOf, anyOf, oneOf, not
objectObjectrequired, additionalProperties, properties, minProperties, maxProperties, patternProperties
arraybyte[] 外的任何数组uniqueItems, additionalItems, items, minItems, maxItems
stringStringminLength, maxLength, 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 是一种通用类型,所有具有类型的 schema 类型都继承自它。它为具有类型的 schema 类型提供了所有 untyped 的 schema 属性。

更多信息,请参阅 $jsonSchema