#405 - Add Example for MongoDB Schema & Validation.

This commit is contained in:
Christoph Strobl
2018-09-11 13:14:02 +02:00
committed by Mark Paluch
parent 04be1b9784
commit 9520ac6a75
8 changed files with 394 additions and 0 deletions

View File

@@ -29,6 +29,7 @@
<module>security</module>
<module>text-search</module>
<module>transactions</module>
<module>schema-validation</module>
<module>util</module>
</modules>
@@ -59,6 +60,7 @@
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>provided</scope>
<version>2.1.1</version>
</dependency>
</dependencies>

View File

@@ -0,0 +1,71 @@
# Spring Data MongoDB 2.1 - Schema & Validation Example
MongoDB (as of version 3.2) supports validating documents against a given structure described by a query.
```json
{
"name" : {
"$exists" : true,
"$ne" : null,
"$type" : 2
},
"age" : {
"$exists" : true,
"$ne" : null,
"$type" : 16,
"$gte" : 0,
"$lte" : 125
}
}
```
The structure can be built from `Criteria` objects in the same way as they are used for defining queries.
```java
Validator.criteria(where("name").exists(true).ne(null).type(2)
.and("age").exists(true).ne(null).type(16).gte(0).lte(125));
```
MongoDB 3.6 supports collections that validate documents against a provided [JSON Schema](https://docs.mongodb.com/manual/core/schema-validation/#json-schema) that
complies to the JSON schema specification (draft 4).
```json
{
"type": "object",
"required": [ "name", "age" ],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"age": {
"type": "int",
"minimum" : 0,
"exclusiveMinimum" : false,
"maximum" : 125,
"exclusiveMaximum" : false
}
}
}
```
The `MongoJsonSchema` and its builder allow fluent schema definition via the Java API.
```java
MongoJsonSchema schema = MongoJsonSchema.builder() //
.required("name", "age") //
.properties( //
string("name").minLength(1), //
int32("age").gte(0).lte(125) //
).build();
```
The schema can not only be used to set up `Document` validation for a collection
```java
template.createCollection(Jedi.class, CollectionOptions.empty().validator(Validator.schema(schema)));
```
but also to query the store for documents matching a given blueprint.
```java
template.find(query(matchingDocumentStructure(schema)), Jedi.class);
```

View File

@@ -0,0 +1,34 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-mongodb-schema-validation</artifactId>
<name>Spring Data MongoDB 2.1 - Schema &amp; Validation Example</name>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-mongodb-examples</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<properties>
<mongodb.version>3.8.0</mongodb.version>
<mongo-driver-reactivestreams.version>1.9.0</mongo-driver-reactivestreams.version>
<spring-data-releasetrain.version>Lovelace-RC2</spring-data-releasetrain.version>
</properties>
<profiles>
<!-- Override property as the module always needs Lovelace -->
<profile>
<id>spring-data-next</id>
<properties>
<spring-data-releasetrain.version>Lovelace-RC2</spring-data-releasetrain.version>
</properties>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.schema;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("star-wars")
class Jedi {
@Id String id;
@Nullable String name;
@Nullable String lastname;
@Nullable Integer age;
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.schema;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.mongodb.core.CollectionOptions;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Christoph Strobl
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class DocumentValidation {
static final String COLLECTION = "star-wars";
@Autowired MongoOperations mongoOps;
@Before
public void setUp() {
mongoOps.dropCollection(COLLECTION);
}
/**
* MongoDB (as of version 3.2) supports validating documents against a given structure described by a query. The
* structure can be built from {@link org.springframework.data.mongodb.core.query.Criteria} objects in the same way as
* they are used for defining queries.
*
* <pre>
* <code>
* {
* name : {
* $exists : true,
* $ne : null,
* $type : 2
* },
* age : {
* $exists : true,
* $ne : null,
* $type : 16,
* $gte : 0,
* $lte : 125
* }
* }
* </code>
* </pre>
*/
@Test
public void criteriaValidator() {
Validator validator = Validator.criteria( //
where("name").exists(true).ne(null).type(2) // non null String
.and("age").exists(true).ne(null).type(16).gte(0).lte(125)) // non null int between 0 and 125
;
mongoOps.createCollection(Jedi.class, CollectionOptions.empty().validator(validator));
assertThat(mongoOps.save(new Jedi("luke", "luke", "skywalker", 25))).isNotNull();
assertThatExceptionOfType(DataIntegrityViolationException.class)
.isThrownBy(() -> mongoOps.save(new Jedi("yoda", "yoda", null, 900)));
}
/**
* As of version 3.6, MongoDB supports collections that validate documents against a provided JSON Schema that
* complies to the JSON schema specification (draft 4).
*
* <pre>
* <code>
* {
* "type": "object",
* "required": [ "name", "age" ],
* "properties": {
* "name": {
* "type": "string",
* "minLength": 1
* },
* "age": {
* "type": "int",
* "minimum" : 0,
* "exclusiveMinimum" : false,
* "maximum" : 125,
* "exclusiveMaximum" : false
* }
* }
* }
* </code>
* </pre>
*/
@Test
public void schemaValidator() {
Validator validator = Validator.schema(MongoJsonSchema.builder() //
.required("name", "age") //
.properties( //
string("name").minLength(1), //
int32("age").gte(0).lte(125) //
).build());
mongoOps.createCollection(Jedi.class, CollectionOptions.empty().validator(validator));
assertThat(mongoOps.save(new Jedi("luke", "luke", "skywalker", 25))).isNotNull();
assertThatExceptionOfType(DataIntegrityViolationException.class)
.isThrownBy(() -> mongoOps.save(new Jedi("yoda", "yoda", null, 900)));
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.schema;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Christoph Strobl
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SchemaQuery {
static final String COLLECTION = "star-wars";
@Autowired MongoOperations mongoOps;
@Before
public void setUp() {
mongoOps.dropCollection(COLLECTION);
}
/**
* As of version 3.6, MongoDB supports querying documents against a provided JSON Schema that complies to the JSON
* schema specification (draft 4).
*
* <pre>
* <code>
* {
* "type": "object",
* "required": [ "name", "age" ],
* "properties": {
* "name": {
* "type": "string",
* "minLength": 1
* },
* "age": {
* "type": "int",
* "minimum" : 0,
* "exclusiveMinimum" : false,
* "maximum" : 125,
* "exclusiveMaximum" : false
* }
* }
* }
* </code>
* </pre>
*/
@Test
public void criteriaValidator() {
Jedi luke = new Jedi("luke", "luke", "skywalker", 25);
Jedi yoda = new Jedi("yoda", "yoda", null, 900);
mongoOps.save(luke);
mongoOps.save(yoda);
MongoJsonSchema schema = MongoJsonSchema.builder() //
.required("name", "age") //
.properties( //
string("name").minLength(1), //
int32("age").gte(0).lte(125) //
).build();
assertThat(mongoOps.find(query(matchingDocumentStructure(schema)), Jedi.class)).containsExactly(luke);
}
}

View File

@@ -0,0 +1,2 @@
spring.mongodb.embedded.features=ONLY_64BIT,NO_HTTP_INTERFACE_ARG,NO_CHUNKSIZE_ARG,SYNC_DELAY,ONLY_WITH_SSL
spring.mongodb.embedded.version=4.0.1