diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java index 0f28588d5..c1c430117 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java @@ -21,7 +21,7 @@ import java.util.Optional; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.schema.MongoJsonSchema; -import org.springframework.data.mongodb.core.validation.ValidatorDefinition; +import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.data.util.Optionals; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -43,7 +43,7 @@ public class CollectionOptions { private @Nullable Long size; private @Nullable Boolean capped; private @Nullable Collation collation; - private Validator validator; + private ValidationOptions validationOptions; /** * Constructs a new CollectionOptions instance. @@ -56,17 +56,17 @@ public class CollectionOptions { */ @Deprecated public CollectionOptions(@Nullable Long size, @Nullable Long maxDocuments, @Nullable Boolean capped) { - this(size, maxDocuments, capped, null, Validator.none()); + this(size, maxDocuments, capped, null, ValidationOptions.none()); } private CollectionOptions(@Nullable Long size, @Nullable Long maxDocuments, @Nullable Boolean capped, - @Nullable Collation collation, Validator validator) { + @Nullable Collation collation, ValidationOptions validationOptions) { this.maxDocuments = maxDocuments; this.size = size; this.capped = capped; this.collation = collation; - this.validator = validator; + this.validationOptions = validationOptions; } /** @@ -80,7 +80,7 @@ public class CollectionOptions { Assert.notNull(collation, "Collation must not be null!"); - return new CollectionOptions(null, null, null, collation, Validator.none()); + return new CollectionOptions(null, null, null, collation, ValidationOptions.none()); } /** @@ -90,7 +90,7 @@ public class CollectionOptions { * @since 2.0 */ public static CollectionOptions empty() { - return new CollectionOptions(null, null, null, null, Validator.none()); + return new CollectionOptions(null, null, null, null, ValidationOptions.none()); } /** @@ -101,7 +101,7 @@ public class CollectionOptions { * @since 2.0 */ public CollectionOptions capped() { - return new CollectionOptions(size, maxDocuments, true, collation, validator); + return new CollectionOptions(size, maxDocuments, true, collation, validationOptions); } /** @@ -112,7 +112,7 @@ public class CollectionOptions { * @since 2.0 */ public CollectionOptions maxDocuments(long maxDocuments) { - return new CollectionOptions(size, maxDocuments, capped, collation, validator); + return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions); } /** @@ -123,7 +123,7 @@ public class CollectionOptions { * @since 2.0 */ public CollectionOptions size(long size) { - return new CollectionOptions(size, maxDocuments, capped, collation, validator); + return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions); } /** @@ -134,11 +134,11 @@ public class CollectionOptions { * @since 2.0 */ public CollectionOptions collation(@Nullable Collation collation) { - return new CollectionOptions(size, maxDocuments, capped, collation, validator); + return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions); } /** - * Create new {@link CollectionOptions} with already given settings and {@code validator} set to given + * Create new {@link CollectionOptions} with already given settings and {@code validationOptions} set to given * {@link MongoJsonSchema}. * * @param schema can be {@literal null}. @@ -146,11 +146,19 @@ public class CollectionOptions { * @since 2.1 */ public CollectionOptions schema(@Nullable MongoJsonSchema schema) { - return validation(new Validator(schema, null, validator.validationLevel, validator.validationAction)); + return validator(Validator.schema(schema)); } - public CollectionOptions validatorDefinition(@Nullable ValidatorDefinition definition) { - return validation(new Validator(null, definition, validator.validationLevel, validator.validationAction)); + /** + * /** Create new {@link CollectionOptions} with already given settings and {@code validationOptions} set to given + * {@link Validator}. + * + * @param validator can be {@literal null}. + * @return new {@link CollectionOptions}. + * @since 2.1 + */ + public CollectionOptions validator(@Nullable Validator validator) { + return validation(validationOptions.validator(validator)); } /** @@ -219,7 +227,7 @@ public class CollectionOptions { public CollectionOptions schemaValidationLevel(ValidationLevel validationLevel) { Assert.notNull(validationLevel, "ValidationLevel must not be null!"); - return validation(new Validator(validator.schema, validator.validatorDefinition, validationLevel, validator.validationAction)); + return validation(validationOptions.validationLevel(validationLevel)); } /** @@ -233,20 +241,20 @@ public class CollectionOptions { public CollectionOptions schemaValidationAction(ValidationAction validationAction) { Assert.notNull(validationAction, "ValidationAction must not be null!"); - return validation(new Validator(validator.schema, validator.validatorDefinition, validator.validationLevel, validationAction)); + return validation(validationOptions.validationAction(validationAction)); } /** - * Create new {@link CollectionOptions} with the given {@link Validator}. + * Create new {@link CollectionOptions} with the given {@link ValidationOptions}. * - * @param validator must not be {@literal null}. Use {@link Validator#none()} to remove validation. + * @param validationOptions must not be {@literal null}. Use {@link ValidationOptions#none()} to remove validation. * @return new {@link CollectionOptions}. * @since 2.1 */ - public CollectionOptions validation(Validator validator) { + public CollectionOptions validation(ValidationOptions validationOptions) { - Assert.notNull(validator, "Validator must not be null!"); - return new CollectionOptions(size, maxDocuments, capped, collation, validator); + Assert.notNull(validationOptions, "ValidationOptions must not be null!"); + return new CollectionOptions(size, maxDocuments, capped, collation, validationOptions); } /** @@ -293,47 +301,72 @@ public class CollectionOptions { * @return {@link Optional#empty()} if not set. * @since 2.1 */ - public Optional getValidator() { - return validator.isEmpty() ? Optional.empty() : Optional.of(validator); + public Optional getValidationOptions() { + return validationOptions.isEmpty() ? Optional.empty() : Optional.of(validationOptions); } /** - * Encapsulation of Validator options. + * Encapsulation of ValidationOptions options. * * @author Christoph Strobl * @author Andreas Zink * @since 2.1 */ @RequiredArgsConstructor - public static class Validator { + public static class ValidationOptions { - private static final Validator NONE = new Validator(null, null, null, null); + private static final ValidationOptions NONE = new ValidationOptions(null, null, null); - private final @Nullable MongoJsonSchema schema; - private final @Nullable ValidatorDefinition validatorDefinition; + private final @Nullable Validator validator; private final @Nullable ValidationLevel validationLevel; private final @Nullable ValidationAction validationAction; /** - * Create an empty {@link Validator}. + * Create an empty {@link ValidationOptions}. * * @return never {@literal null}. */ - public static Validator none() { + public static ValidationOptions none() { return NONE; } /** - * Get the {@code $jsonSchema} used for validation. + * Define the {@link Validator} to be used for document validation. * - * @return {@link Optional#empty()} if not set. + * @param validator can be {@literal null}. + * @return new instance of {@link ValidationOptions}. */ - public Optional getSchema() { - return Optional.ofNullable(schema); + public ValidationOptions validator(@Nullable Validator validator) { + return new ValidationOptions(validator, validationLevel, validationAction); } - public Optional getValidatorDefinition() { - return Optional.ofNullable(validatorDefinition); + /** + * Define the validation level to apply. + * + * @param validationLevel can be {@literal null}. + * @return new instance of {@link ValidationOptions}. + */ + public ValidationOptions validationLevel(ValidationLevel validationLevel) { + return new ValidationOptions(validator, validationLevel, validationAction); + } + + /** + * Define the validation action to take. + * + * @param validationAction can be {@literal null}. + * @return new instance of {@link ValidationOptions}. + */ + public ValidationOptions validationAction(ValidationAction validationAction) { + return new ValidationOptions(validator, validationLevel, validationAction); + } + + /** + * Get the {@link Validator} to use. + * + * @return never {@literal null}. + */ + public Optional getValidator() { + return Optional.ofNullable(validator); } /** @@ -358,7 +391,7 @@ public class CollectionOptions { * @return {@literal true} if no arguments set. */ boolean isEmpty() { - return !Optionals.isAnyPresent(getSchema(), getValidatorDefinition(), getValidationAction(), getValidationLevel()); + return !Optionals.isAnyPresent(getValidator(), getValidationAction(), getValidationLevel()); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 2d2f7d5a2..b83a66878 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -63,7 +63,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.BulkOperations.BulkMode; -import org.springframework.data.mongodb.core.CollectionOptions.Validator; +import org.springframework.data.mongodb.core.CollectionOptions.ValidationOptions; import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; @@ -108,6 +108,8 @@ import org.springframework.data.mongodb.core.query.Meta; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.mongodb.core.validation.JsonSchemaValidator; +import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.data.mongodb.util.MongoClientVersion; import org.springframework.data.projection.ProjectionInformation; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -139,10 +141,16 @@ import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoIterable; -import com.mongodb.client.model.*; +import com.mongodb.client.model.CountOptions; +import com.mongodb.client.model.CreateCollectionOptions; +import com.mongodb.client.model.DeleteOptions; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.FindOneAndDeleteOptions; +import com.mongodb.client.model.FindOneAndUpdateOptions; +import com.mongodb.client.model.ReturnDocument; +import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.ValidationAction; import com.mongodb.client.model.ValidationLevel; -import com.mongodb.client.model.ValidationOptions; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; import com.mongodb.util.JSONParseException; @@ -2236,7 +2244,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, if (collectionOptions.containsKey("validator")) { - ValidationOptions options = new ValidationOptions(); + com.mongodb.client.model.ValidationOptions options = new com.mongodb.client.model.ValidationOptions(); if (collectionOptions.containsKey("validationLevel")) { options.validationLevel(ValidationLevel.fromString(collectionOptions.getString("validationLevel"))); @@ -2377,10 +2385,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, Document doc = convertToDocument(collectionOptions); - if (collectionOptions != null && collectionOptions.getValidator().isPresent()) { + if (collectionOptions != null && collectionOptions.getValidationOptions().isPresent()) { - Validator v = collectionOptions.getValidator().get(); - v.getSchema().ifPresent(val -> doc.put("validator", schemaMapper.mapSchema(val.toDocument(), targetType))); + ValidationOptions v = collectionOptions.getValidationOptions().get(); + v.getValidator().ifPresent(val -> doc.put("validator", getMappedValidator(val, targetType))); } return doc; } @@ -2401,18 +2409,28 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, collectionOptions.getMaxDocuments().ifPresent(val -> document.put("max", val)); collectionOptions.getCollation().ifPresent(val -> document.append("collation", val.toDocument())); - if (collectionOptions.getValidator().isPresent()) { - Validator v = collectionOptions.getValidator().get(); + if (collectionOptions.getValidationOptions().isPresent()) { + + CollectionOptions.ValidationOptions v = collectionOptions.getValidationOptions().get(); v.getValidationLevel().ifPresent(val -> document.append("validationLevel", val.getValue())); v.getValidationAction().ifPresent(val -> document.append("validationAction", val.getValue())); - v.getSchema().ifPresent(val -> document.append("validator", - new MongoJsonSchemaMapper(getConverter()).mapSchema(val.toDocument(), Object.class))); - v.getValidatorDefinition().ifPresent(val -> document.put("validator", val.toDocument())); + v.getValidator().ifPresent(val -> document.append("validator", getMappedValidator(val, Object.class))); } } return document; } + Document getMappedValidator(Validator validator, Class domainType) { + + Document validationRules = validator.toDocument(); + + if (validator instanceof JsonSchemaValidator || validationRules.containsKey("$jsonSchema")) { + return schemaMapper.mapSchema(validationRules, domainType); + } + + return queryMapper.getMappedObject(validationRules, mappingContext.getPersistentEntity(domainType)); + } + /** * Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter. * The first document that matches the query is returned and also removed from the collection in the database. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index 71fffaa92..3a70e89c0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -24,8 +24,17 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.util.function.Tuple2; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -91,6 +100,8 @@ import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.mongodb.core.validation.JsonSchemaValidator; +import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.data.mongodb.util.MongoClientVersion; import org.springframework.data.projection.ProjectionInformation; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -2007,15 +2018,14 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati collectionOptions.getMaxDocuments().ifPresent(result::maxDocuments); collectionOptions.getCollation().map(Collation::toMongoCollation).ifPresent(result::collation); - collectionOptions.getValidator().ifPresent(it -> { + collectionOptions.getValidationOptions().ifPresent(it -> { ValidationOptions validationOptions = new ValidationOptions(); it.getValidationAction().ifPresent(validationOptions::validationAction); it.getValidationLevel().ifPresent(validationOptions::validationLevel); - it.getSchema() - .ifPresent(val -> validationOptions.validator(schemaMapper.mapSchema(val.toDocument(), entityType))); + it.getValidator().ifPresent(val -> validationOptions.validator(getMappedValidator(val, entityType))); result.validationOptions(validationOptions); }); @@ -2023,6 +2033,17 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return result; } + Document getMappedValidator(Validator validator, Class domainType) { + + Document validationRules = validator.toDocument(); + + if (validator instanceof JsonSchemaValidator || validationRules.containsKey("$jsonSchema")) { + return schemaMapper.mapSchema(validationRules, domainType); + } + + return queryMapper.getMappedObject(validationRules, mappingContext.getPersistentEntity(domainType)); + } + /** * Map the results of an ad-hoc query on the default MongoDB collection to an object using the template's converter. * The first document that matches the query is returned and also removed from the collection in the database. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java index c6cc30385..c6d150a2b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * 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. @@ -15,51 +15,61 @@ */ package org.springframework.data.mongodb.core.validation; +import lombok.AccessLevel; import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; import org.bson.Document; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.CriteriaDefinition; -import org.springframework.lang.NonNull; +import org.springframework.data.mongodb.core.query.SerializationUtils; import org.springframework.util.Assert; /** - * Utility to build a MongoDB {@code validator} based on a {@link CriteriaDefinition}. - * + * {@link Validator} implementation based on {@link CriteriaDefinition query expressions}. + * * @author Andreas Zink + * @author Christoph Strobl * @since 2.1 * @see Criteria + * @see Schema Validation */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) @EqualsAndHashCode -public class CriteriaValidator implements ValidatorDefinition { +public class CriteriaValidator implements Validator { - private final Document document; - - private CriteriaValidator(Document document) { - Assert.notNull(document, "Document must not be null!"); - this.document = document; - } + private final CriteriaDefinition criteria; /** - * Builds a {@code validator} object, which is basically setup of query operators, based on a + * Creates a new {@link Validator} object, which is basically setup of query operators, based on a * {@link CriteriaDefinition} instance. - * - * @param criteria the criteria to build the {@code validator} from - * @return + * + * @param criteria the criteria to build the {@code validator} from. Must not be {@literal null}. + * @return new instance of {@link CriteriaValidator}. + * @throws IllegalArgumentException when criteria is {@literal null}. */ - public static CriteriaValidator fromCriteria(@NonNull CriteriaDefinition criteria) { + public static CriteriaValidator of(CriteriaDefinition criteria) { + Assert.notNull(criteria, "Criteria must not be null!"); - return new CriteriaValidator(criteria.getCriteriaObject()); + return new CriteriaValidator(criteria); } + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.validation.Validator#toDocument() + */ @Override public Document toDocument() { - return this.document; + return criteria.getCriteriaObject(); } + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ @Override public String toString() { - return document.toString(); + return SerializationUtils.serializeToJsonSafely(toDocument()); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java new file mode 100644 index 000000000..870973c0f --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java @@ -0,0 +1,70 @@ +/* + * 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 org.springframework.data.mongodb.core.validation; + +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; + +import org.bson.Document; +import org.springframework.data.mongodb.core.query.SerializationUtils; +import org.springframework.util.Assert; + +/** + * Most trivial {@link Validator} implementation using plain {@link Document} to describe the desired document structure + * which can be either a {@code $jsonSchema} or query expression. + * + * @author Christoph Strobl + * @since 2.1 + * @see Schema Validation + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@EqualsAndHashCode +public class DocumentValidator implements Validator { + + private final Document validatorObject; + + /** + * Create new {@link org.springframework.data.mongodb.core.validation.DocumentValidator} defining validation rules via + * a plain {@link Document}. + * + * @param validatorObject must not be {@literal null}. + * @throws IllegalArgumentException if validatorObject is {@literal null}. + */ + public static DocumentValidator of(Document validatorObject) { + + Assert.notNull(validatorObject, "ValidatorObject must not be null!"); + return new DocumentValidator(new Document(validatorObject)); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.validation.Validator#toDocument() + */ + @Override + public Document toDocument() { + return new Document(validatorObject); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return SerializationUtils.serializeToJsonSafely(validatorObject); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java new file mode 100644 index 000000000..3c1fbbf9c --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java @@ -0,0 +1,67 @@ +/* + * 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 org.springframework.data.mongodb.core.validation; + +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; + +import org.bson.Document; +import org.springframework.data.mongodb.core.query.SerializationUtils; +import org.springframework.data.mongodb.core.schema.MongoJsonSchema; +import org.springframework.util.Assert; + +/** + * {@link Validator} implementation based on {@link MongoJsonSchema JSON Schema}. + * + * @author Christoph Strobl + * @since 2.1 + * @see Schema Validation + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@EqualsAndHashCode +public class JsonSchemaValidator implements Validator { + + private final MongoJsonSchema schema; + + public static JsonSchemaValidator of(MongoJsonSchema schema) { + + Assert.notNull(schema, "Schema must not be null!"); + return new JsonSchemaValidator(schema); + } + + public MongoJsonSchema getSchema() { + return schema; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.validation.Validator#toDocument() + */ + @Override + public Document toDocument() { + return schema.toDocument(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return SerializationUtils.serializeToJsonSafely(toDocument()); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationAction.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationAction.java deleted file mode 100644 index 199e00a7d..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationAction.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 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 org.springframework.data.mongodb.core.validation; - -import lombok.Getter; - -/** - * Determines whether to error on invalid documents or just warn about the violations but allow invalid documents to be - * inserted. - * - * @author Andreas Zink - * @since 2.1 - * @see MongoDB Collection Options - */ -public enum ValidationAction { - - /** - * Documents must pass validation before the write occurs. Otherwise, the write operation fails. (MongoDB default) - */ - ERROR("error"), - - /** - * Documents do not have to pass validation. If the document fails validation, the write operation logs the validation - * failure. - */ - WARN("warn"); - - @Getter private String value; - - private ValidationAction(String value) { - this.value = value; - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationLevel.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationLevel.java deleted file mode 100644 index 187c63ca1..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationLevel.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 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 org.springframework.data.mongodb.core.validation; - -import lombok.Getter; - -/** - * Determines how strictly MongoDB applies the validation rules to existing documents during an update. - * - * @author Andreas Zink - * @since 2.1 - * @see MongoDB Collection Options - */ -public enum ValidationLevel { - - /** - * No validation for inserts or updates. - */ - OFF("off"), - - /** - * Apply validation rules to all inserts and all updates. (MongoDB default) - */ - STRICT("strict"), - - /** - * Apply validation rules to inserts and to updates on existing valid documents. Do not apply rules to updates on - * existing invalid documents. - */ - MODERATE("moderate"); - - @Getter private String value; - - private ValidationLevel(String value) { - this.value = value; - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationOptions.java deleted file mode 100644 index ebb9c0baa..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidationOptions.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 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 org.springframework.data.mongodb.core.validation; - -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.Optional; - -import org.springframework.data.mongodb.core.CollectionOptions; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -/** - * Wraps the collection validation options. - * - * @author Andreas Zink - * @since 2.1 - * @see {@link CollectionOptions} - * @see MongoDB Document Validation - * @see MongoDB Collection Options - */ -@EqualsAndHashCode -@ToString -public class ValidationOptions { - private ValidatorDefinition validator; - private ValidationLevel validationLevel; - private ValidationAction validationAction; - - private ValidationOptions(ValidatorDefinition validator) { - Assert.notNull(validator, "ValidatorDefinition must not be null!"); - this.validator = validator; - } - - public static ValidationOptions validator(@NonNull ValidatorDefinition validator) { - return new ValidationOptions(validator); - } - - public ValidationOptions validationLevel(@Nullable ValidationLevel validationLevel) { - this.validationLevel = validationLevel; - return this; - } - - public ValidationOptions validationAction(@Nullable ValidationAction validationAction) { - this.validationAction = validationAction; - return this; - } - - public ValidatorDefinition getValidator() { - return validator; - } - - public Optional getValidationLevel() { - return Optional.ofNullable(validationLevel); - } - - public Optional getValidationAction() { - return Optional.ofNullable(validationAction); - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/Validator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/Validator.java new file mode 100644 index 000000000..40dfd2994 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/Validator.java @@ -0,0 +1,81 @@ +/* + * 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 org.springframework.data.mongodb.core.validation; + +import org.bson.Document; +import org.springframework.data.mongodb.core.query.CriteriaDefinition; +import org.springframework.data.mongodb.core.schema.MongoJsonSchema; +import org.springframework.util.Assert; + +/** + * Provides a {@code validator} object to be used for collection validation via + * {@link org.springframework.data.mongodb.core.CollectionOptions.ValidationOptions}. + * + * @author Andreas Zink + * @author Christoph Strobl + * @since 2.1 + * @see MongoDB Collection Options + */ +public interface Validator { + + /** + * Get the {@link Document} containing the validation specific rules. The document may contain fields that may require + * type and/or field name mapping. + * + * @return a MongoDB {@code validator} {@link Document}. Never {@literal null}. + */ + Document toDocument(); + + /** + * Creates a basic {@link Validator} checking documents against a given set of rules. + * + * @param validationRules must not be {@literal null}. + * @return new instance of {@link Validator}. + * @throws IllegalArgumentException if validationRules is {@literal null}. + */ + static Validator document(Document validationRules) { + + Assert.notNull(validationRules, "ValidationRules must not be null!"); + return DocumentValidator.of(validationRules); + } + + /** + * Creates a new {@link Validator} checking documents against the structure defined in {@link MongoJsonSchema}. + * + * @param schema must not be {@literal null}. + * @return new instance of {@link Validator}. + * @throws IllegalArgumentException if schema is {@literal null}. + */ + static Validator schema(MongoJsonSchema schema) { + + Assert.notNull(schema, "Schema must not be null!"); + return JsonSchemaValidator.of(schema); + } + + /** + * Creates a new {@link Validator} checking documents against a given query structure expressed by + * {@link CriteriaDefinition}.
+ * + * @param criteria must not be {@literal null}. + * @return new instance of {@link Validator}. + * @throws IllegalArgumentException if criteria is {@literal null}. + */ + static Validator criteria(CriteriaDefinition criteria) { + + Assert.notNull(criteria, "Criteria must not be null!"); + return CriteriaValidator.of(criteria); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidatorDefinition.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidatorDefinition.java deleted file mode 100644 index 50bcbb579..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/ValidatorDefinition.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 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 org.springframework.data.mongodb.core.validation; - -import org.bson.Document; -import org.springframework.lang.NonNull; - -/** - * Provides a {@code validator} object to be used for collection validation. - * - * @author Andreas Zink - * @since 2.1 - * @see MongoDB Collection Options - */ -public interface ValidatorDefinition { - - /** - * @return a MongoDB {@code validator} document - */ - public @NonNull Document toDocument(); - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java new file mode 100644 index 000000000..002a4ee1f --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java @@ -0,0 +1,6 @@ +/** + * MongoDB schema validation specifics. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.data.mongodb.core.validation; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java index 10db45c82..a2449427b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * 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. @@ -15,46 +15,50 @@ */ package org.springframework.data.mongodb.core; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.mongodb.core.query.Criteria.*; +import static org.springframework.data.mongodb.core.validation.Validator.*; import lombok.AllArgsConstructor; import lombok.Data; -import lombok.NoArgsConstructor; import java.util.List; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; - import org.bson.Document; -import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.mongodb.config.AbstractMongoConfiguration; +import org.springframework.data.mongodb.core.CollectionOptions.ValidationOptions; +import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.validation.CriteriaValidator; -import org.springframework.data.mongodb.core.validation.ValidationAction; -import org.springframework.data.mongodb.core.validation.ValidationLevel; -import org.springframework.data.mongodb.core.validation.ValidationOptions; +import org.springframework.data.mongodb.core.validation.DocumentValidator; import org.springframework.data.mongodb.test.util.MongoVersionRule; import org.springframework.data.util.Version; +import org.springframework.lang.Nullable; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.mongodb.MongoClient; +import com.mongodb.client.model.ValidationAction; +import com.mongodb.client.model.ValidationLevel; /** + * Integration tests for {@link CollectionOptions#validation(ValidationOptions)} using + * {@link org.springframework.data.mongodb.core.validation.CriteriaValidator} and {@link DocumentValidator}. + * * @author Andreas Zink + * @author Christoph Strobl */ @RunWith(SpringJUnit4ClassRunner.class) public class MongoTemplateValidationTests { public static @ClassRule MongoVersionRule REQUIRES_AT_LEAST_3_2_0 = MongoVersionRule.atLeast(Version.parse("3.2.0")); - public static final String COLLECTION_NAME = "validation-1"; + + static final String COLLECTION_NAME = "validation-1"; @Configuration static class Config extends AbstractMongoConfiguration { @@ -79,73 +83,99 @@ public class MongoTemplateValidationTests { @Test // DATAMONGO-1322 public void testCollectionWithSimpleCriteriaBasedValidation() { - Criteria criteria = Criteria.where("nonNullString").ne(null).type(2).and("rangedInteger").ne(null).type(16).gte(0) - .lte(122); - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().validatorDefinition(CriteriaValidator.fromCriteria(criteria))); + + Criteria criteria = where("nonNullString").ne(null).type(2).and("rangedInteger").ne(null).type(16).gte(0).lte(122); + + template.createCollection(COLLECTION_NAME, CollectionOptions.empty().validator(criteria(criteria))); Document validator = getValidatorInfo(COLLECTION_NAME); + assertThat(validator.get("nonNullString")).isEqualTo(new Document("$ne", null).append("$type", 2)); assertThat(validator.get("rangedInteger")) .isEqualTo(new Document("$ne", null).append("$type", 16).append("$gte", 0).append("$lte", 122)); - template.save(new SimpleBean("hello", 101), COLLECTION_NAME); - try { - template.save(new SimpleBean(null, 101), COLLECTION_NAME); - Assert.fail("The collection validation was setup to check for non-null string"); - } catch (Exception e) { - // ignore - } - try { - template.save(new SimpleBean("hello", -1), COLLECTION_NAME); - Assert.fail("The collection validation was setup to check for non-negative int"); - } catch (Exception e) { - // ignore - } + template.save(new SimpleBean("hello", 101, null), COLLECTION_NAME); + + assertThatExceptionOfType(DataIntegrityViolationException.class) + .isThrownBy(() -> template.save(new SimpleBean(null, 101, null), COLLECTION_NAME)); + + assertThatExceptionOfType(DataIntegrityViolationException.class) + .isThrownBy(() -> template.save(new SimpleBean("hello", -1, null), COLLECTION_NAME)); } @Test // DATAMONGO-1322 public void testCollectionValidationActionError() { - Criteria criteria = Criteria.where("name").type(2); - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationAction(com.mongodb.client.model.ValidationAction.ERROR).validatorDefinition(CriteriaValidator.fromCriteria(criteria))); - String validationAction = getValidationActionInfo(COLLECTION_NAME); - assertThat(ValidationAction.ERROR.getValue()).isEqualTo(validationAction); + template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationAction(ValidationAction.ERROR) + .validator(criteria(where("name").type(2)))); + + assertThat(getValidationActionInfo(COLLECTION_NAME)).isEqualTo(ValidationAction.ERROR.getValue()); } @Test // DATAMONGO-1322 public void testCollectionValidationActionWarn() { - Criteria criteria = Criteria.where("name").type(2); - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationAction(com.mongodb.client.model.ValidationAction.WARN).validatorDefinition(CriteriaValidator.fromCriteria(criteria))); - String validationAction = getValidationActionInfo(COLLECTION_NAME); - assertThat(ValidationAction.WARN.getValue()).isEqualTo(validationAction); + template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationAction(ValidationAction.WARN) + .validator(criteria(where("name").type(2)))); + + assertThat(getValidationActionInfo(COLLECTION_NAME)).isEqualTo(ValidationAction.WARN.getValue()); } @Test // DATAMONGO-1322 public void testCollectionValidationLevelOff() { - Criteria criteria = Criteria.where("name").type(2); - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(com.mongodb.client.model.ValidationLevel.OFF).validatorDefinition(CriteriaValidator.fromCriteria(criteria))); - String validationAction = getValidationLevelInfo(COLLECTION_NAME); - assertThat(ValidationLevel.OFF.getValue()).isEqualTo(validationAction); + template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(ValidationLevel.OFF) + .validator(criteria(where("name").type(2)))); + + assertThat(getValidationLevelInfo(COLLECTION_NAME)).isEqualTo(ValidationLevel.OFF.getValue()); } @Test // DATAMONGO-1322 public void testCollectionValidationLevelModerate() { - Criteria criteria = Criteria.where("name").type(2); - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(com.mongodb.client.model.ValidationLevel.MODERATE).validatorDefinition(CriteriaValidator.fromCriteria(criteria))); - String validationAction = getValidationLevelInfo(COLLECTION_NAME); - assertThat(ValidationLevel.MODERATE.getValue()).isEqualTo(validationAction); + template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(ValidationLevel.MODERATE) + .validator(criteria(where("name").type(2)))); + + assertThat(getValidationLevelInfo(COLLECTION_NAME)).isEqualTo(ValidationLevel.MODERATE.getValue()); } @Test // DATAMONGO-1322 public void testCollectionValidationLevelStrict() { - Criteria criteria = Criteria.where("name").type(2); - template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(com.mongodb.client.model.ValidationLevel.STRICT).validatorDefinition(CriteriaValidator.fromCriteria(criteria))); - String validationAction = getValidationLevelInfo(COLLECTION_NAME); - assertThat(ValidationLevel.STRICT.getValue()).isEqualTo(validationAction); + template.createCollection(COLLECTION_NAME, CollectionOptions.empty().schemaValidationLevel(ValidationLevel.STRICT) + .validator(criteria(where("name").type(2)))); + + assertThat(getValidationLevelInfo(COLLECTION_NAME)).isEqualTo(ValidationLevel.STRICT.getValue()); + } + + @Test // DATAMONGO-1322 + public void mapsFieldNameCorrectlyWhenGivenDomainTypeInformation() { + + template.createCollection(SimpleBean.class, + CollectionOptions.empty().validator(criteria(where("customFieldName").type(8)))); + + assertThat(getValidatorInfo(COLLECTION_NAME)).isEqualTo(new Document("customName", new Document("$type", 8))); + } + + @Test // DATAMONGO-1322 + public void usesDocumentValidatorCorrectly() { + + Document rules = new Document("customFieldName", new Document("$type", "bool")); + + template.createCollection(COLLECTION_NAME, CollectionOptions.empty().validator(document(rules))); + + assertThat(getValidatorInfo(COLLECTION_NAME)) + .isEqualTo(new Document("customFieldName", new Document("$type", "bool"))); + } + + @Test // DATAMONGO-1322 + public void mapsDocumentValidatorFieldsCorrectly() { + + Document rules = new Document("customFieldName", new Document("$type", "bool")); + + template.createCollection(SimpleBean.class, CollectionOptions.empty().validator(document(rules))); + + assertThat(getValidatorInfo(COLLECTION_NAME)).isEqualTo(new Document("customName", new Document("$type", "bool"))); } private Document getCollectionOptions(String collectionName) { @@ -165,6 +195,7 @@ public class MongoTemplateValidationTests { } private Document getCollectionInfo(String collectionName) { + return template.execute(db -> { Document result = db.runCommand( new Document().append("listCollections", 1).append("filter", new Document("name", collectionName))); @@ -174,10 +205,12 @@ public class MongoTemplateValidationTests { @Data @AllArgsConstructor - @NoArgsConstructor + @org.springframework.data.mongodb.core.mapping.Document(collection = COLLECTION_NAME) static class SimpleBean { - @NotNull private String nonNullString; - @NotNull @Min(0) @Max(122) private Integer rangedInteger; + + private @Nullable String nonNullString; + private @Nullable Integer rangedInteger; + private @Field("customName") Object customFieldName; } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java index 0ce070ae2..ff8b9ff79 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaTests.java @@ -19,6 +19,7 @@ import static org.springframework.data.mongodb.test.util.Assertions.*; import lombok.Data; +import java.util.Collections; import java.util.List; import org.bson.Document; @@ -33,6 +34,7 @@ import org.springframework.data.mongodb.core.CollectionOptions; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper; import org.springframework.data.mongodb.core.mapping.Field; +import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.data.mongodb.test.util.MongoVersionRule; import org.springframework.data.util.Version; import org.springframework.test.context.ContextConfiguration; @@ -99,6 +101,20 @@ public class MongoJsonSchemaTests { assertThat(fromDb).isEqualTo($jsonSchema); } + @Test // DATAMONGO-1835 + public void writeSchemaInDocumentValidatorCorrectly() { + + Document unmappedSchema = new Document("$jsonSchema", + new Document("type", "object").append("required", Collections.singletonList("firstname"))); + + Document mappedSchema = new Document("$jsonSchema", + new Document("type", "object").append("required", Collections.singletonList("first_name"))); + + template.createCollection(Person.class, CollectionOptions.empty().validator(Validator.document(unmappedSchema))); + + assertThat(readSchemaFromDatabase("persons")).isEqualTo(mappedSchema); + } + @Test // DATAMONGO-1835 public void nonMappedSchema() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorTest.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorUnitTests.java similarity index 84% rename from spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorTest.java rename to spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorUnitTests.java index 127968043..2a4c8dd11 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorTest.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/validation/CriteriaValidatorUnitTests.java @@ -22,14 +22,18 @@ import org.junit.Test; import org.springframework.data.mongodb.core.query.Criteria; /** + * Unit tests for {@link CriteriaValidator}. + * * @author Andreas Zink + * @author Christoph Strobl */ -public class CriteriaValidatorTest { +public class CriteriaValidatorUnitTests { @Test // DATAMONGO-1322 public void testSimpleCriteria() { + Criteria criteria = Criteria.where("nonNullString").ne(null).type(2).and("rangedInteger").type(16).gte(0).lte(122); - Document validator = CriteriaValidator.fromCriteria(criteria).toDocument(); + Document validator = CriteriaValidator.of(criteria).toDocument(); assertThat(validator.get("nonNullString")).isEqualTo(new Document("$ne", null).append("$type", 2)); assertThat(validator.get("rangedInteger")) @@ -38,6 +42,6 @@ public class CriteriaValidatorTest { @Test(expected = IllegalArgumentException.class) // DATAMONGO-1322 public void testFailOnNull() { - CriteriaValidator.fromCriteria(null); + CriteriaValidator.of(null); } } diff --git a/src/main/asciidoc/reference/mongo-3.adoc b/src/main/asciidoc/reference/mongo-3.adoc index 281a63a60..37ac56a93 100644 --- a/src/main/asciidoc/reference/mongo-3.adoc +++ b/src/main/asciidoc/reference/mongo-3.adoc @@ -96,6 +96,23 @@ MongoDB 3.6 allows validation and querying of documents using JSON schema draft Spring Data MongoDB supports MongoDB's specific JSON schema implementation to define and use schemas. See <> for further details. +[[mongo.mongo-3.validation.query-expression]] +==== Query Expression Validation + +Next to the <> as of version 3.2 MongoDB supports validating documents against a given query structure. The structure can be built using `Criteria` objects just the same way as they are used for defining queries. + +[source,java] +---- +Criteria queryExpression = Criteria.where("lastname").ne(null).type(2) + .and("age").ne(null).type(16).gt(0).lte(150); + +Validator validator = Validator.criteria(queryExpression); + +template.createCollection(Person.class, CollectionOptions.empty().validator(validator)); +---- + +NOTE: Field names used within the query expression are mapped to the domain types property names taking potential `@Field` annotations into account. + [[mongo.mongo-3.misc]] === Other things to be aware of