Skip to content

Commit f2bb467

Browse files
committed
DATAMONGO-1835 - Polishing.
Add JSON schema to collection creation using reactive API. Refactor MongoJsonSchema to interface with default implementations for JsonSchemaObject and Document-based schemas. Make fields final and methods static where possible. Add minItems/maxItems/additionalItems properties to ArrayJsonSchemaProperty. Add missing overrides to NullJsonSchemaProperty. Slightly rename methods for item/property counts. Add generics, Javadoc, minor tweaks. Original pull request: #524.
1 parent 365430c commit f2bb467

18 files changed

+949
-316
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java

+16-20
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.mongodb.core;
1717

18+
import lombok.RequiredArgsConstructor;
19+
1820
import java.util.Optional;
1921

2022
import org.springframework.data.mongodb.core.query.Collation;
@@ -146,7 +148,8 @@ public CollectionOptions schema(@Nullable MongoJsonSchema schema) {
146148
}
147149

148150
/**
149-
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to {@code off}.
151+
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to
152+
* {@link ValidationLevel#OFF}.
150153
*
151154
* @return new {@link CollectionOptions}.
152155
* @since 2.1
@@ -156,7 +159,8 @@ public CollectionOptions disableValidation() {
156159
}
157160

158161
/**
159-
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to {@code strict}.
162+
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to
163+
* {@link ValidationLevel#STRICT}.
160164
*
161165
* @return new {@link CollectionOptions}.
162166
* @since 2.1
@@ -167,7 +171,7 @@ public CollectionOptions strictValidation() {
167171

168172
/**
169173
* Create new {@link CollectionOptions} with already given settings and {@code validationLevel} set to
170-
* {@code moderate}.
174+
* {@link ValidationLevel#MODERATE}.
171175
*
172176
* @return new {@link CollectionOptions}.
173177
* @since 2.1
@@ -177,7 +181,8 @@ public CollectionOptions moderateValidation() {
177181
}
178182

179183
/**
180-
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to {@code warn}.
184+
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to
185+
* {@link ValidationAction#WARN}.
181186
*
182187
* @return new {@link CollectionOptions}.
183188
* @since 2.1
@@ -187,7 +192,8 @@ public CollectionOptions warnOnValidationError() {
187192
}
188193

189194
/**
190-
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to {@code error}.
195+
* Create new {@link CollectionOptions} with already given settings and {@code validationAction} set to
196+
* {@link ValidationAction#ERROR}.
191197
*
192198
* @return new {@link CollectionOptions}.
193199
* @since 2.1
@@ -291,25 +297,18 @@ public Optional<Validator> getValidator() {
291297
* @author Christoph Strobl
292298
* @since 2.1
293299
*/
300+
@RequiredArgsConstructor
294301
public static class Validator {
295302

296303
private static final Validator NONE = new Validator(null, null, null);
297304

298-
private @Nullable MongoJsonSchema schema;
299-
private @Nullable ValidationLevel validationLevel;
300-
private @Nullable ValidationAction validationAction;
301-
302-
private Validator(@Nullable MongoJsonSchema schema, @Nullable ValidationLevel validationLevel,
303-
@Nullable ValidationAction validationAction) {
304-
305-
this.schema = schema;
306-
this.validationLevel = validationLevel;
307-
this.validationAction = validationAction;
308-
}
305+
private final @Nullable MongoJsonSchema schema;
306+
private final @Nullable ValidationLevel validationLevel;
307+
private final @Nullable ValidationAction validationAction;
309308

310309
/**
311310
* Create an empty {@link Validator}.
312-
*
311+
*
313312
* @return never {@literal null}.
314313
*/
315314
public static Validator none() {
@@ -321,7 +320,6 @@ public static Validator none() {
321320
*
322321
* @return {@link Optional#empty()} if not set.
323322
*/
324-
@Nullable
325323
public Optional<MongoJsonSchema> getSchema() {
326324
return Optional.ofNullable(schema);
327325
}
@@ -331,7 +329,6 @@ public Optional<MongoJsonSchema> getSchema() {
331329
*
332330
* @return {@link Optional#empty()} if not set.
333331
*/
334-
@Nullable
335332
public Optional<ValidationLevel> getValidationLevel() {
336333
return Optional.ofNullable(validationLevel);
337334
}
@@ -341,7 +338,6 @@ public Optional<ValidationLevel> getValidationLevel() {
341338
*
342339
* @return @return {@link Optional#empty()} if not set.
343340
*/
344-
@Nullable
345341
public Optional<ValidationAction> getValidationAction() {
346342
return Optional.ofNullable(validationAction);
347343
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@
124124
import org.springframework.util.ResourceUtils;
125125
import org.springframework.util.StringUtils;
126126

127+
import com.mongodb.DBCollection;
128+
import com.mongodb.DBCursor;
129+
import com.mongodb.Mongo;
127130
import com.mongodb.MongoClient;
128131
import com.mongodb.MongoException;
129132
import com.mongodb.ReadPreference;
@@ -2370,7 +2373,7 @@ protected Document convertToDocument(@Nullable CollectionOptions collectionOptio
23702373

23712374
Document doc = convertToDocument(collectionOptions);
23722375

2373-
if (collectionOptions.getValidator().isPresent()) {
2376+
if (collectionOptions != null && collectionOptions.getValidator().isPresent()) {
23742377
Validator v = collectionOptions.getValidator().get();
23752378
v.getSchema().ifPresent(val -> doc.put("validator", schemaMapper.mapSchema(val.toDocument(), targetType)));
23762379
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java

+35-21
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,7 @@
6969
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
7070
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
7171
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
72-
import org.springframework.data.mongodb.core.convert.DbRefProxyHandler;
73-
import org.springframework.data.mongodb.core.convert.DbRefResolver;
74-
import org.springframework.data.mongodb.core.convert.DbRefResolverCallback;
75-
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
76-
import org.springframework.data.mongodb.core.convert.MongoConverter;
77-
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
78-
import org.springframework.data.mongodb.core.convert.MongoWriter;
79-
import org.springframework.data.mongodb.core.convert.QueryMapper;
80-
import org.springframework.data.mongodb.core.convert.UpdateMapper;
72+
import org.springframework.data.mongodb.core.convert.*;
8173
import org.springframework.data.mongodb.core.index.IndexOperationsAdapter;
8274
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
8375
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
@@ -125,6 +117,7 @@
125117
import com.mongodb.client.model.FindOneAndUpdateOptions;
126118
import com.mongodb.client.model.ReturnDocument;
127119
import com.mongodb.client.model.UpdateOptions;
120+
import com.mongodb.client.model.ValidationOptions;
128121
import com.mongodb.client.result.DeleteResult;
129122
import com.mongodb.client.result.UpdateResult;
130123
import com.mongodb.reactivestreams.client.AggregatePublisher;
@@ -176,6 +169,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
176169
private final PersistenceExceptionTranslator exceptionTranslator;
177170
private final QueryMapper queryMapper;
178171
private final UpdateMapper updateMapper;
172+
private final JsonSchemaMapper schemaMapper;
179173
private final SpelAwareProxyProjectionFactory projectionFactory;
180174

181175
private @Nullable WriteConcern writeConcern;
@@ -220,6 +214,7 @@ public ReactiveMongoTemplate(ReactiveMongoDatabaseFactory mongoDatabaseFactory,
220214
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter() : mongoConverter;
221215
this.queryMapper = new QueryMapper(this.mongoConverter);
222216
this.updateMapper = new UpdateMapper(this.mongoConverter);
217+
this.schemaMapper = new MongoJsonSchemaMapper(this.mongoConverter);
223218
this.projectionFactory = new SpelAwareProxyProjectionFactory();
224219

225220
// We always have a mapping context in the converter, whether it's a simple one or not
@@ -486,23 +481,24 @@ public <T> Mono<MongoCollection<Document>> createCollection(Class<T> entityClass
486481
*/
487482
public <T> Mono<MongoCollection<Document>> createCollection(Class<T> entityClass,
488483
@Nullable CollectionOptions collectionOptions) {
489-
return createCollection(determineCollectionName(entityClass), collectionOptions);
484+
return doCreateCollection(determineCollectionName(entityClass),
485+
convertToCreateCollectionOptions(collectionOptions, entityClass));
490486
}
491487

492488
/*
493489
* (non-Javadoc)
494490
* @see org.springframework.data.mongodb.core.ReactiveMongoOperations#createCollection(java.lang.String)
495491
*/
496-
public Mono<MongoCollection<Document>> createCollection(final String collectionName) {
492+
public Mono<MongoCollection<Document>> createCollection(String collectionName) {
497493
return doCreateCollection(collectionName, new CreateCollectionOptions());
498494
}
499495

500496
/*
501497
* (non-Javadoc)
502498
* @see org.springframework.data.mongodb.core.ReactiveMongoOperations#createCollection(java.lang.String, org.springframework.data.mongodb.core.CollectionOptions)
503499
*/
504-
public Mono<MongoCollection<Document>> createCollection(final String collectionName,
505-
final CollectionOptions collectionOptions) {
500+
public Mono<MongoCollection<Document>> createCollection(String collectionName,
501+
@Nullable CollectionOptions collectionOptions) {
506502
return doCreateCollection(collectionName, convertToCreateCollectionOptions(collectionOptions));
507503
}
508504

@@ -814,8 +810,7 @@ protected <O> Flux<O> aggregate(Aggregation aggregation, String collectionName,
814810
}
815811

816812
ReadDocumentCallback<O> readCallback = new ReadDocumentCallback<>(mongoConverter, outputType, collectionName);
817-
return execute(collectionName,
818-
collection -> aggregateAndMap(collection, pipeline, options, readCallback));
813+
return execute(collectionName, collection -> aggregateAndMap(collection, pipeline, options, readCallback));
819814
}
820815

821816
private <O> Flux<O> aggregateAndMap(MongoCollection<Document> collection, List<Document> pipeline,
@@ -1995,17 +1990,36 @@ private Document addFieldsForProjection(Document fields, Class<?> domainType, Cl
19951990
}
19961991

19971992
protected CreateCollectionOptions convertToCreateCollectionOptions(@Nullable CollectionOptions collectionOptions) {
1993+
return convertToCreateCollectionOptions(collectionOptions, Object.class);
1994+
}
19981995

1999-
CreateCollectionOptions result = new CreateCollectionOptions();
1996+
protected CreateCollectionOptions convertToCreateCollectionOptions(@Nullable CollectionOptions collectionOptions,
1997+
Class<?> entityType) {
20001998

2001-
if (collectionOptions != null) {
1999+
CreateCollectionOptions result = new CreateCollectionOptions();
20022000

2003-
collectionOptions.getCapped().ifPresent(result::capped);
2004-
collectionOptions.getSize().ifPresent(result::sizeInBytes);
2005-
collectionOptions.getMaxDocuments().ifPresent(result::maxDocuments);
2006-
collectionOptions.getCollation().map(Collation::toMongoCollation).ifPresent(result::collation);
2001+
if (collectionOptions == null) {
2002+
return result;
20072003
}
20082004

2005+
collectionOptions.getCapped().ifPresent(result::capped);
2006+
collectionOptions.getSize().ifPresent(result::sizeInBytes);
2007+
collectionOptions.getMaxDocuments().ifPresent(result::maxDocuments);
2008+
collectionOptions.getCollation().map(Collation::toMongoCollation).ifPresent(result::collation);
2009+
2010+
collectionOptions.getValidator().ifPresent(it -> {
2011+
2012+
ValidationOptions validationOptions = new ValidationOptions();
2013+
2014+
it.getValidationAction().ifPresent(validationOptions::validationAction);
2015+
it.getValidationLevel().ifPresent(validationOptions::validationLevel);
2016+
2017+
it.getSchema()
2018+
.ifPresent(val -> validationOptions.validator(schemaMapper.mapSchema(val.toDocument(), entityType)));
2019+
2020+
result.validationOptions(validationOptions);
2021+
});
2022+
20092023
return result;
20102024
}
20112025

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* provided domain type.
3535
*
3636
* @author Christoph Strobl
37+
* @author Mark Paluch
3738
* @since 2.1
3839
*/
3940
public class MongoJsonSchemaMapper implements JsonSchemaMapper {
@@ -60,7 +61,7 @@ public MongoJsonSchemaMapper(MongoConverter converter) {
6061
}
6162

6263
/*
63-
* (non-Javadoc)
64+
* (non-Javadoc)
6465
* @see org.springframework.data.mongodb.core.convert.JsonSchemaMapper#mapSchema(org.springframework.data.mongodb.core.schema.MongoJsonSchema, java.lang.Class)
6566
*/
6667
public Document mapSchema(Document jsonSchema, Class<?> type) {
@@ -78,7 +79,8 @@ public Document mapSchema(Document jsonSchema, Class<?> type) {
7879
mapSchemaObject(mappingContext.getPersistentEntity(type), jsonSchema.get($JSON_SCHEMA, Document.class)));
7980
}
8081

81-
private Document mapSchemaObject(@Nullable PersistentEntity entity, Document source) {
82+
@SuppressWarnings("unchecked")
83+
private Document mapSchemaObject(@Nullable PersistentEntity<?, MongoPersistentProperty> entity, Document source) {
8284

8385
Document sink = new Document(source);
8486

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.schema;
17+
18+
import lombok.AllArgsConstructor;
19+
import lombok.NonNull;
20+
21+
import org.bson.Document;
22+
23+
/**
24+
* Value object representing a MongoDB-specific JSON schema which is the default {@link MongoJsonSchema} implementation.
25+
*
26+
* @author Christoph Strobl
27+
* @author Mark Paluch
28+
* @since 2.1
29+
*/
30+
@AllArgsConstructor
31+
class DefaultMongoJsonSchema implements MongoJsonSchema {
32+
33+
private final @NonNull JsonSchemaObject root;
34+
35+
/*
36+
* (non-Javadoc)
37+
* @see org.springframework.data.mongodb.core.schema.MongoJsonSchema#toDocument()
38+
*/
39+
@Override
40+
public Document toDocument() {
41+
return new Document("$jsonSchema", root.toDocument());
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.schema;
17+
18+
import lombok.AllArgsConstructor;
19+
import lombok.NonNull;
20+
21+
import org.bson.Document;
22+
23+
/**
24+
* JSON schema backed by a {@link org.bson.Document} object.
25+
*
26+
* @author Mark Paluch
27+
* @since 2.1
28+
*/
29+
@AllArgsConstructor
30+
class DocumentJsonSchema implements MongoJsonSchema {
31+
32+
private final @NonNull Document document;
33+
34+
/*
35+
* (non-Javadoc)
36+
* @see org.springframework.data.mongodb.core.schema.MongoJsonSchema#toDocument()
37+
*/
38+
@Override
39+
public Document toDocument() {
40+
return new Document("$jsonSchema", new Document(document));
41+
}
42+
}

0 commit comments

Comments
 (0)