From 9520ac6a7563bda05f6ff3d4fbd77be6f23b542d Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 11 Sep 2018 13:14:02 +0200 Subject: [PATCH] #405 - Add Example for MongoDB Schema & Validation. --- mongodb/pom.xml | 2 + mongodb/schema-validation/README.md | 71 ++++++++++ mongodb/schema-validation/pom.xml | 34 +++++ .../mongodb/schema/Application.java | 21 +++ .../springdata/mongodb/schema/Jedi.java | 39 ++++++ .../mongodb/schema/DocumentValidation.java | 132 ++++++++++++++++++ .../mongodb/schema/SchemaQuery.java | 93 ++++++++++++ .../src/test/resources/application.properties | 2 + 8 files changed, 394 insertions(+) create mode 100644 mongodb/schema-validation/README.md create mode 100644 mongodb/schema-validation/pom.xml create mode 100644 mongodb/schema-validation/src/main/java/example/springdata/mongodb/schema/Application.java create mode 100644 mongodb/schema-validation/src/main/java/example/springdata/mongodb/schema/Jedi.java create mode 100644 mongodb/schema-validation/src/test/java/example/springdata/mongodb/schema/DocumentValidation.java create mode 100644 mongodb/schema-validation/src/test/java/example/springdata/mongodb/schema/SchemaQuery.java create mode 100644 mongodb/schema-validation/src/test/resources/application.properties diff --git a/mongodb/pom.xml b/mongodb/pom.xml index 907a68f2..a2daf322 100644 --- a/mongodb/pom.xml +++ b/mongodb/pom.xml @@ -29,6 +29,7 @@ security text-search transactions + schema-validation util @@ -59,6 +60,7 @@ de.flapdoodle.embed de.flapdoodle.embed.mongo provided + 2.1.1 diff --git a/mongodb/schema-validation/README.md b/mongodb/schema-validation/README.md new file mode 100644 index 00000000..03be93f5 --- /dev/null +++ b/mongodb/schema-validation/README.md @@ -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); +``` diff --git a/mongodb/schema-validation/pom.xml b/mongodb/schema-validation/pom.xml new file mode 100644 index 00000000..6dacaddd --- /dev/null +++ b/mongodb/schema-validation/pom.xml @@ -0,0 +1,34 @@ + + 4.0.0 + + spring-data-mongodb-schema-validation + + Spring Data MongoDB 2.1 - Schema & Validation Example + + + org.springframework.data.examples + spring-data-mongodb-examples + 2.0.0.BUILD-SNAPSHOT + + + + 3.8.0 + 1.9.0 + Lovelace-RC2 + + + + + + + + spring-data-next + + Lovelace-RC2 + + + + + + \ No newline at end of file diff --git a/mongodb/schema-validation/src/main/java/example/springdata/mongodb/schema/Application.java b/mongodb/schema-validation/src/main/java/example/springdata/mongodb/schema/Application.java new file mode 100644 index 00000000..9f257760 --- /dev/null +++ b/mongodb/schema-validation/src/main/java/example/springdata/mongodb/schema/Application.java @@ -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 {} diff --git a/mongodb/schema-validation/src/main/java/example/springdata/mongodb/schema/Jedi.java b/mongodb/schema-validation/src/main/java/example/springdata/mongodb/schema/Jedi.java new file mode 100644 index 00000000..dffcfb95 --- /dev/null +++ b/mongodb/schema-validation/src/main/java/example/springdata/mongodb/schema/Jedi.java @@ -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; +} diff --git a/mongodb/schema-validation/src/test/java/example/springdata/mongodb/schema/DocumentValidation.java b/mongodb/schema-validation/src/test/java/example/springdata/mongodb/schema/DocumentValidation.java new file mode 100644 index 00000000..3882aa46 --- /dev/null +++ b/mongodb/schema-validation/src/test/java/example/springdata/mongodb/schema/DocumentValidation.java @@ -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. + * + *
+	 *     
+	 * {
+	 *     name : {
+	 *         $exists : true,
+	 *         $ne : null,
+	 *         $type : 2
+	 *     },
+	 *     age : {
+	 *         $exists : true,
+	 *         $ne : null,
+	 *         $type : 16,
+	 *         $gte : 0,
+	 *         $lte : 125
+	 *     }
+	 * }
+	 *     
+	 * 
+ */ + @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). + * + *
+	 *     
+	 * {
+	 *   "type": "object",
+	 *   "required": [ "name", "age" ],
+	 *   "properties": {
+	 *     "name": {
+	 *       "type": "string",
+	 *       "minLength": 1
+	 *     },
+	 *     "age": {
+	 *       "type": "int",
+	 *       "minimum" : 0,
+	 *       "exclusiveMinimum" : false,
+	 *       "maximum" : 125,
+	 *       "exclusiveMaximum" : false
+	 *     }
+	 *   }
+	 * }
+	 *     
+	 * 
+ */ + @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))); + } +} diff --git a/mongodb/schema-validation/src/test/java/example/springdata/mongodb/schema/SchemaQuery.java b/mongodb/schema-validation/src/test/java/example/springdata/mongodb/schema/SchemaQuery.java new file mode 100644 index 00000000..d155c0f3 --- /dev/null +++ b/mongodb/schema-validation/src/test/java/example/springdata/mongodb/schema/SchemaQuery.java @@ -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). + * + *
+	 *     
+	 * {
+	 *   "type": "object",
+	 *   "required": [ "name", "age" ],
+	 *   "properties": {
+	 *     "name": {
+	 *       "type": "string",
+	 *       "minLength": 1
+	 *     },
+	 *     "age": {
+	 *       "type": "int",
+	 *       "minimum" : 0,
+	 *       "exclusiveMinimum" : false,
+	 *       "maximum" : 125,
+	 *       "exclusiveMaximum" : false
+	 *     }
+	 *   }
+	 * }
+	 *     
+	 * 
+ */ + @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); + } +} diff --git a/mongodb/schema-validation/src/test/resources/application.properties b/mongodb/schema-validation/src/test/resources/application.properties new file mode 100644 index 00000000..8ddb707b --- /dev/null +++ b/mongodb/schema-validation/src/test/resources/application.properties @@ -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 \ No newline at end of file