Skip to content

Commit 612845f

Browse files
committed
Polishing.
Extract CreateCollectionOptions conversion to EntityOperations to unify collection creation. Adopt tests. See #3984 Original pull request: #3990.
1 parent 1f06954 commit 612845f

File tree

6 files changed

+191
-173
lines changed

6 files changed

+191
-173
lines changed

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

+92-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Optional;
2222

2323
import org.bson.Document;
24+
2425
import org.springframework.core.convert.ConversionService;
2526
import org.springframework.dao.InvalidDataAccessApiUsageException;
2627
import org.springframework.data.convert.CustomConversions;
@@ -32,7 +33,9 @@
3233
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
3334
import org.springframework.data.mongodb.core.CollectionOptions.TimeSeriesOptions;
3435
import org.springframework.data.mongodb.core.convert.MongoConverter;
36+
import org.springframework.data.mongodb.core.convert.MongoJsonSchemaMapper;
3537
import org.springframework.data.mongodb.core.convert.MongoWriter;
38+
import org.springframework.data.mongodb.core.convert.QueryMapper;
3639
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
3740
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
3841
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
@@ -41,9 +44,11 @@
4144
import org.springframework.data.mongodb.core.query.Criteria;
4245
import org.springframework.data.mongodb.core.query.Query;
4346
import org.springframework.data.mongodb.core.timeseries.Granularity;
47+
import org.springframework.data.mongodb.core.validation.Validator;
4448
import org.springframework.data.projection.EntityProjection;
4549
import org.springframework.data.projection.EntityProjectionIntrospector;
4650
import org.springframework.data.projection.ProjectionFactory;
51+
import org.springframework.data.util.Optionals;
4752
import org.springframework.lang.Nullable;
4853
import org.springframework.util.Assert;
4954
import org.springframework.util.ClassUtils;
@@ -52,6 +57,10 @@
5257
import org.springframework.util.ObjectUtils;
5358
import org.springframework.util.StringUtils;
5459

60+
import com.mongodb.client.model.CreateCollectionOptions;
61+
import com.mongodb.client.model.TimeSeriesGranularity;
62+
import com.mongodb.client.model.ValidationOptions;
63+
5564
/**
5665
* Common operations performed on an entity in the context of it's mapping metadata.
5766
*
@@ -67,20 +76,31 @@ class EntityOperations {
6776
private static final String ID_FIELD = "_id";
6877

6978
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context;
79+
private final QueryMapper queryMapper;
7080

7181
private final EntityProjectionIntrospector introspector;
7282

83+
private final MongoJsonSchemaMapper schemaMapper;
84+
7385
EntityOperations(MongoConverter converter) {
74-
this(converter.getMappingContext(), converter.getCustomConversions(), converter.getProjectionFactory());
86+
this(converter, new QueryMapper(converter));
87+
}
88+
89+
EntityOperations(MongoConverter converter, QueryMapper queryMapper) {
90+
this(converter, converter.getMappingContext(), converter.getCustomConversions(), converter.getProjectionFactory(),
91+
queryMapper);
7592
}
7693

77-
EntityOperations(MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context,
78-
CustomConversions conversions, ProjectionFactory projectionFactory) {
94+
EntityOperations(MongoConverter converter,
95+
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context,
96+
CustomConversions conversions, ProjectionFactory projectionFactory, QueryMapper queryMapper) {
7997
this.context = context;
98+
this.queryMapper = queryMapper;
8099
this.introspector = EntityProjectionIntrospector.create(projectionFactory,
81100
EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
82101
.and(((target, underlyingType) -> !conversions.isSimpleType(target))),
83102
context);
103+
this.schemaMapper = new MongoJsonSchemaMapper(converter);
84104
}
85105

86106
/**
@@ -259,6 +279,75 @@ public <M, D> EntityProjection<M, D> introspectProjection(Class<M> resultType, C
259279
return introspector.introspect(resultType, entityType);
260280
}
261281

282+
/**
283+
* Convert {@link CollectionOptions} to {@link CreateCollectionOptions} using {@link Class entityType} to obtain
284+
* mapping metadata.
285+
*
286+
* @param collectionOptions
287+
* @param entityType
288+
* @return
289+
* @since 3.4
290+
*/
291+
public CreateCollectionOptions convertToCreateCollectionOptions(@Nullable CollectionOptions collectionOptions,
292+
Class<?> entityType) {
293+
294+
Optional<Collation> collation = Optionals.firstNonEmpty(
295+
() -> Optional.ofNullable(collectionOptions).flatMap(CollectionOptions::getCollation),
296+
() -> forType(entityType).getCollation());//
297+
298+
CreateCollectionOptions result = new CreateCollectionOptions();
299+
collation.map(Collation::toMongoCollation).ifPresent(result::collation);
300+
301+
if (collectionOptions == null) {
302+
return result;
303+
}
304+
305+
collectionOptions.getCapped().ifPresent(result::capped);
306+
collectionOptions.getSize().ifPresent(result::sizeInBytes);
307+
collectionOptions.getMaxDocuments().ifPresent(result::maxDocuments);
308+
collectionOptions.getCollation().map(Collation::toMongoCollation).ifPresent(result::collation);
309+
310+
collectionOptions.getValidationOptions().ifPresent(it -> {
311+
312+
ValidationOptions validationOptions = new ValidationOptions();
313+
314+
it.getValidationAction().ifPresent(validationOptions::validationAction);
315+
it.getValidationLevel().ifPresent(validationOptions::validationLevel);
316+
317+
it.getValidator().ifPresent(val -> validationOptions.validator(getMappedValidator(val, entityType)));
318+
319+
result.validationOptions(validationOptions);
320+
});
321+
322+
collectionOptions.getTimeSeriesOptions().map(forType(entityType)::mapTimeSeriesOptions).ifPresent(it -> {
323+
324+
com.mongodb.client.model.TimeSeriesOptions options = new com.mongodb.client.model.TimeSeriesOptions(
325+
it.getTimeField());
326+
327+
if (StringUtils.hasText(it.getMetaField())) {
328+
options.metaField(it.getMetaField());
329+
}
330+
if (!Granularity.DEFAULT.equals(it.getGranularity())) {
331+
options.granularity(TimeSeriesGranularity.valueOf(it.getGranularity().name().toUpperCase()));
332+
}
333+
334+
result.timeSeriesOptions(options);
335+
});
336+
337+
return result;
338+
}
339+
340+
private Document getMappedValidator(Validator validator, Class<?> domainType) {
341+
342+
Document validationRules = validator.toDocument();
343+
344+
if (validationRules.containsKey("$jsonSchema")) {
345+
return schemaMapper.mapSchema(validationRules, domainType);
346+
}
347+
348+
return queryMapper.getMappedObject(validationRules, context.getPersistentEntity(domainType));
349+
}
350+
262351
/**
263352
* A representation of information about an entity.
264353
*

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

+65-51
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ public MongoTemplate(MongoDatabaseFactory mongoDbFactory, @Nullable MongoConvert
224224
this.queryMapper = new QueryMapper(this.mongoConverter);
225225
this.updateMapper = new UpdateMapper(this.mongoConverter);
226226
this.schemaMapper = new MongoJsonSchemaMapper(this.mongoConverter);
227-
this.operations = new EntityOperations(this.mongoConverter);
227+
this.operations = new EntityOperations(this.mongoConverter, this.queryMapper);
228228
this.propertyOperations = new PropertyOperations(this.mongoConverter.getMappingContext());
229229
this.queryOperations = new QueryOperations(queryMapper, updateMapper, operations, propertyOperations,
230230
mongoDbFactory);
@@ -595,13 +595,8 @@ public <T> MongoCollection<Document> createCollection(Class<T> entityClass,
595595

596596
Assert.notNull(entityClass, "EntityClass must not be null!");
597597

598-
CollectionOptions options = collectionOptions != null ? collectionOptions : CollectionOptions.empty();
599-
options = Optionals
600-
.firstNonEmpty(() -> Optional.ofNullable(collectionOptions).flatMap(CollectionOptions::getCollation),
601-
() -> operations.forType(entityClass).getCollation()) //
602-
.map(options::collation).orElse(options);
603-
604-
return doCreateCollection(getCollectionName(entityClass), convertToDocument(options, entityClass));
598+
return doCreateCollection(getCollectionName(entityClass),
599+
operations.convertToCreateCollectionOptions(collectionOptions, entityClass));
605600
}
606601

607602
@Override
@@ -617,7 +612,8 @@ public MongoCollection<Document> createCollection(String collectionName,
617612
@Nullable CollectionOptions collectionOptions) {
618613

619614
Assert.notNull(collectionName, "CollectionName must not be null!");
620-
return doCreateCollection(collectionName, convertToDocument(collectionOptions, Object.class));
615+
return doCreateCollection(collectionName,
616+
operations.convertToCreateCollectionOptions(collectionOptions, Object.class));
621617
}
622618

623619
@Override
@@ -2225,64 +2221,82 @@ protected <T> T maybeCallAfterConvert(T object, Document document, String collec
22252221
*/
22262222
@SuppressWarnings("ConstantConditions")
22272223
protected MongoCollection<Document> doCreateCollection(String collectionName, Document collectionOptions) {
2224+
return doCreateCollection(collectionName, getCreateCollectionOptions(collectionOptions));
2225+
}
2226+
2227+
/**
2228+
* Create the specified collection using the provided options
2229+
*
2230+
* @param collectionName
2231+
* @param collectionOptions
2232+
* @return the collection that was created
2233+
* @since 3.3.3
2234+
*/
2235+
@SuppressWarnings("ConstantConditions")
2236+
protected MongoCollection<Document> doCreateCollection(String collectionName,
2237+
CreateCollectionOptions collectionOptions) {
2238+
22282239
return execute(db -> {
22292240

2230-
CreateCollectionOptions co = new CreateCollectionOptions();
2241+
db.createCollection(collectionName, collectionOptions);
22312242

2232-
if (collectionOptions.containsKey("capped")) {
2233-
co.capped((Boolean) collectionOptions.get("capped"));
2234-
}
2235-
if (collectionOptions.containsKey("size")) {
2236-
co.sizeInBytes(((Number) collectionOptions.get("size")).longValue());
2237-
}
2238-
if (collectionOptions.containsKey("max")) {
2239-
co.maxDocuments(((Number) collectionOptions.get("max")).longValue());
2240-
}
2243+
MongoCollection<Document> coll = db.getCollection(collectionName, Document.class);
22412244

2242-
if (collectionOptions.containsKey("collation")) {
2243-
co.collation(IndexConverters.fromDocument(collectionOptions.get("collation", Document.class)));
2245+
// TODO: Emit a collection created event
2246+
if (LOGGER.isDebugEnabled()) {
2247+
LOGGER.debug(String.format("Created collection [%s]",
2248+
coll.getNamespace() != null ? coll.getNamespace().getCollectionName() : collectionName));
22442249
}
2250+
return coll;
2251+
});
2252+
}
22452253

2246-
if (collectionOptions.containsKey("validator")) {
2254+
private CreateCollectionOptions getCreateCollectionOptions(Document document) {
22472255

2248-
com.mongodb.client.model.ValidationOptions options = new com.mongodb.client.model.ValidationOptions();
2256+
CreateCollectionOptions options = new CreateCollectionOptions();
22492257

2250-
if (collectionOptions.containsKey("validationLevel")) {
2251-
options.validationLevel(ValidationLevel.fromString(collectionOptions.getString("validationLevel")));
2252-
}
2253-
if (collectionOptions.containsKey("validationAction")) {
2254-
options.validationAction(ValidationAction.fromString(collectionOptions.getString("validationAction")));
2255-
}
2258+
if (document.containsKey("capped")) {
2259+
options.capped((Boolean) document.get("capped"));
2260+
}
2261+
if (document.containsKey("size")) {
2262+
options.sizeInBytes(((Number) document.get("size")).longValue());
2263+
}
2264+
if (document.containsKey("max")) {
2265+
options.maxDocuments(((Number) document.get("max")).longValue());
2266+
}
22562267

2257-
options.validator(collectionOptions.get("validator", Document.class));
2258-
co.validationOptions(options);
2259-
}
2268+
if (document.containsKey("collation")) {
2269+
options.collation(IndexConverters.fromDocument(document.get("collation", Document.class)));
2270+
}
22602271

2261-
if (collectionOptions.containsKey("timeseries")) {
2272+
if (document.containsKey("validator")) {
22622273

2263-
Document timeSeries = collectionOptions.get("timeseries", Document.class);
2264-
com.mongodb.client.model.TimeSeriesOptions options = new com.mongodb.client.model.TimeSeriesOptions(
2265-
timeSeries.getString("timeField"));
2266-
if (timeSeries.containsKey("metaField")) {
2267-
options.metaField(timeSeries.getString("metaField"));
2268-
}
2269-
if (timeSeries.containsKey("granularity")) {
2270-
options.granularity(TimeSeriesGranularity.valueOf(timeSeries.getString("granularity").toUpperCase()));
2271-
}
2272-
co.timeSeriesOptions(options);
2274+
ValidationOptions validation = new ValidationOptions();
2275+
2276+
if (document.containsKey("validationLevel")) {
2277+
validation.validationLevel(ValidationLevel.fromString(document.getString("validationLevel")));
2278+
}
2279+
if (document.containsKey("validationAction")) {
2280+
validation.validationAction(ValidationAction.fromString(document.getString("validationAction")));
22732281
}
22742282

2275-
db.createCollection(collectionName, co);
2283+
validation.validator(document.get("validator", Document.class));
2284+
options.validationOptions(validation);
2285+
}
22762286

2277-
MongoCollection<Document> coll = db.getCollection(collectionName, Document.class);
2287+
if (document.containsKey("timeseries")) {
22782288

2279-
// TODO: Emit a collection created event
2280-
if (LOGGER.isDebugEnabled()) {
2281-
LOGGER.debug(String.format("Created collection [%s]",
2282-
coll.getNamespace() != null ? coll.getNamespace().getCollectionName() : collectionName));
2289+
Document timeSeries = document.get("timeseries", Document.class);
2290+
TimeSeriesOptions timeseries = new TimeSeriesOptions(timeSeries.getString("timeField"));
2291+
if (timeSeries.containsKey("metaField")) {
2292+
timeseries.metaField(timeSeries.getString("metaField"));
22832293
}
2284-
return coll;
2285-
});
2294+
if (timeSeries.containsKey("granularity")) {
2295+
timeseries.granularity(TimeSeriesGranularity.valueOf(timeSeries.getString("granularity").toUpperCase()));
2296+
}
2297+
options.timeSeriesOptions(timeseries);
2298+
}
2299+
return options;
22862300
}
22872301

22882302
/**

0 commit comments

Comments
 (0)