From 0f3c90bea9ab50c73cec975d5b291e9dc44fe696 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 9 Apr 2019 13:26:04 +0200 Subject: [PATCH] DATAMONGO-1849 - Polishing. Fix generics usage in MappingMongoJsonSchemaCreator. Make fields final. Rename MappingMongoConverter.computeWriteTarget to getWriteTarget and expose it publicly for reuse in custom DefaultTypeMapper setups without the need to subclass MappingMongoConverter. Remove Nullability functionality for required fields as nullability indicators should originate from PersistentProperty and PreferredConstructor. Update documentation. Related ticket: DATACMNS-1513 Original pull request: #733. --- .../core/MappingMongoJsonSchemaCreator.java | 57 ++++++------ .../mongodb/core/MongoJsonSchemaCreator.java | 39 ++++---- .../core/convert/DefaultMongoTypeMapper.java | 45 ++++++++-- .../core/convert/MappingMongoConverter.java | 12 +-- .../data/mongodb/core/mapping/FieldType.java | 7 +- ...appingMongoJsonSchemaCreatorUnitTests.java | 88 +++---------------- src/main/asciidoc/new-features.adoc | 2 +- .../reference/mongo-custom-conversions.adoc | 2 +- .../asciidoc/reference/mongo-json-schema.adoc | 78 ++++++++-------- 9 files changed, 147 insertions(+), 183 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java index 212d5edd3..815a68994 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java @@ -21,12 +21,11 @@ import java.util.Collections; import java.util.EnumSet; import java.util.List; -import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.SimplePropertyHandler; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.mapping.Field; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.ObjectJsonSchemaProperty; import org.springframework.data.mongodb.core.schema.JsonSchemaObject; @@ -35,7 +34,6 @@ import org.springframework.data.mongodb.core.schema.JsonSchemaProperty; import org.springframework.data.mongodb.core.schema.MongoJsonSchema; import org.springframework.data.mongodb.core.schema.MongoJsonSchema.MongoJsonSchemaBuilder; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -45,26 +43,28 @@ import org.springframework.util.ObjectUtils; * {@link MongoJsonSchemaCreator} implementation using both {@link MongoConverter} and {@link MappingContext} to obtain * domain type meta information which considers {@link org.springframework.data.mongodb.core.mapping.Field field names} * and {@link org.springframework.data.mongodb.core.convert.MongoCustomConversions custom conversions}. - * + * * @author Christoph Strobl + * @author Mark Paluch * @since 2.2 */ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { - private MongoConverter converter; - private MappingContext mappingContext; + private final MongoConverter converter; + private final MappingContext, MongoPersistentProperty> mappingContext; /** * Create a new instance of {@link MappingMongoJsonSchemaCreator}. * * @param converter must not be {@literal null}. */ + @SuppressWarnings("unchecked") MappingMongoJsonSchemaCreator(MongoConverter converter) { Assert.notNull(converter, "Converter must not be null!"); this.converter = converter; - this.mappingContext = converter.getMappingContext(); - + this.mappingContext = (MappingContext, MongoPersistentProperty>) converter + .getMappingContext(); } /* @@ -74,7 +74,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { @Override public MongoJsonSchema createSchemaFor(Class type) { - PersistentEntity entity = mappingContext.getPersistentEntity(type); + MongoPersistentEntity entity = mappingContext.getRequiredPersistentEntity(type); MongoJsonSchemaBuilder schemaBuilder = MongoJsonSchema.builder(); List schemaProperties = computePropertiesForEntity(Collections.emptyList(), entity); @@ -84,35 +84,33 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { } - private List computePropertiesForEntity(List path, - PersistentEntity entity) { + private List computePropertiesForEntity(List path, + MongoPersistentEntity entity) { List schemaProperties = new ArrayList<>(); - entity.doWithProperties((SimplePropertyHandler) nested -> { - ArrayList currentPath = new ArrayList<>(path); + for (MongoPersistentProperty nested : entity) { + + List currentPath = new ArrayList<>(path); if (path.contains(nested)) { // cycle guard schemaProperties.add(createSchemaProperty(computePropertyFieldName(CollectionUtils.lastElement(currentPath)), Object.class, false)); - return; + break; } currentPath.add(nested); - JsonSchemaProperty jsonSchemaProperty = computeSchemaForProperty(currentPath, entity); - if (jsonSchemaProperty != null) { - schemaProperties.add(jsonSchemaProperty); - } - }); + schemaProperties.add(computeSchemaForProperty(currentPath)); + } return schemaProperties; } - private JsonSchemaProperty computeSchemaForProperty(List path, PersistentEntity parent) { + private JsonSchemaProperty computeSchemaForProperty(List path) { - PersistentProperty property = CollectionUtils.lastElement(path); + MongoPersistentProperty property = CollectionUtils.lastElement(path); - boolean required = isRequiredProperty(parent, property); + boolean required = isRequiredProperty(property); Class rawTargetType = computeTargetType(property); // target type before conversion Class targetType = converter.getTypeMapper().getWriteTargetTypeFor(rawTargetType); // conversion target type @@ -133,12 +131,12 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { return createSchemaProperty(fieldName, targetType, required); } - private JsonSchemaProperty createObjectSchemaPropertyForEntity(List path, - PersistentProperty property, boolean required) { + private JsonSchemaProperty createObjectSchemaPropertyForEntity(List path, + MongoPersistentProperty property, boolean required) { ObjectJsonSchemaProperty target = JsonSchemaProperty.object(property.getName()); List nestedProperties = computePropertiesForEntity(path, - mappingContext.getPersistentEntity(property)); + mappingContext.getRequiredPersistentEntity(property)); return createPotentiallyRequiredSchemaProperty( target.properties(nestedProperties.toArray(new JsonSchemaProperty[0])), required); @@ -147,6 +145,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { private JsonSchemaProperty createEnumSchemaProperty(String fieldName, Class targetType, boolean required) { List possibleValues = new ArrayList<>(); + for (Object enumValue : EnumSet.allOf((Class) targetType)) { possibleValues.add(converter.convertToMongoType(enumValue)); } @@ -178,10 +177,8 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { : property.getName(); } - private boolean isRequiredProperty(PersistentEntity parent, PersistentProperty property) { - - return (parent.isConstructorArgument(property) && !property.isAnnotationPresent(Nullable.class)) - || property.getType().isPrimitive(); + private boolean isRequiredProperty(PersistentProperty property) { + return property.getType().isPrimitive(); } private Class computeTargetType(PersistentProperty property) { @@ -196,7 +193,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { } if (mongoProperty.hasExplicitWriteTarget()) { - return mongoProperty.findAnnotation(Field.class).targetType().getJavaClass(); + return mongoProperty.getRequiredAnnotation(Field.class).targetType().getJavaClass(); } return mongoProperty.getFieldType() != mongoProperty.getActualType() ? Object.class : mongoProperty.getFieldType(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java index a3cbf48e8..a20b353e0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java @@ -23,29 +23,30 @@ import org.springframework.util.Assert; * {@link MongoJsonSchemaCreator} extracts the {@link MongoJsonSchema} for a given {@link Class} by applying the * following mapping rules. *

- * Required Properties
- * - All Constructor arguments annotated with {@link org.springframework.lang.Nullable}.
- * - Properties of primitive type.
- *

- *

- * Ignored Properties
- * - All properties annotated with {@link org.springframework.data.annotation.Transient}.
- *

- *

- * Property Type Mapping
- * - {@link java.lang.Object} -> {@code type : 'object'}
- * - {@link java.util.Arrays} -> {@code type : 'array'}
- * - {@link java.util.Collection} -> {@code type : 'array'}
- * - {@link java.util.Map} -> {@code type : 'object'}
- * - {@link java.lang.Enum} -> {@code type : 'string', enum : [the enum values]}
- * - Simple Types -> {@code type : 'the corresponding bson type' }
- * - Domain Types -> {@code type : 'object', properties : {the types properties} }
+ * Required Properties + *

    + *
  • Properties of primitive type
  • + *
+ * Ignored Properties + *
    + *
  • All properties annotated with {@link org.springframework.data.annotation.Transient}
  • + *
+ * Property Type Mapping + *
    + *
  • {@link java.lang.Object} -> {@code type : 'object'}
  • + *
  • {@link java.util.Arrays} -> {@code type : 'array'}
  • + *
  • {@link java.util.Collection} -> {@code type : 'array'}
  • + *
  • {@link java.util.Map} -> {@code type : 'object'}
  • + *
  • {@link java.lang.Enum} -> {@code type : 'string', enum : [the enum values]}
  • + *
  • Simple Types -> {@code type : 'the corresponding bson type' }
  • + *
  • Domain Types -> {@code type : 'object', properties : {the types properties} }
  • + *
*
* {@link org.springframework.data.annotation.Id _id} properties using types that can be converted into * {@link org.bson.types.ObjectId} like {@link String} will be mapped to {@code type : 'object'} unless there is more * specific information available via the {@link org.springframework.data.mongodb.core.mapping.MongoId} annotation. *

- * + * * @author Christoph Strobl * @since 2.2 */ @@ -66,7 +67,7 @@ public interface MongoJsonSchemaCreator { * @param mongoConverter must not be {@literal null}. * @return new instance of {@link MongoJsonSchemaCreator}. */ - static MongoJsonSchemaCreator jsonSchemaCreator(MongoConverter mongoConverter) { + static MongoJsonSchemaCreator create(MongoConverter mongoConverter) { Assert.notNull(mongoConverter, "MongoConverter must not be null!"); return new MappingMongoJsonSchemaCreator(mongoConverter); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java index ee3b9aaa2..b16681561 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java @@ -15,14 +15,16 @@ */ package org.springframework.data.mongodb.core.convert; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; +import java.util.function.UnaryOperator; import org.bson.Document; import org.bson.conversions.Bson; + +import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.DefaultTypeMapper; import org.springframework.data.convert.SimpleTypeInformationMapper; import org.springframework.data.convert.TypeAliasAccessor; @@ -59,29 +61,58 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper implements M private final TypeAliasAccessor accessor; private final @Nullable String typeKey; - private Function, Class> writeTarget = it -> it; + private UnaryOperator> writeTarget = UnaryOperator.identity(); + /** + * Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code _class}. + */ public DefaultMongoTypeMapper() { this(DEFAULT_TYPE_KEY); } + /** + * Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code typeKey}. + * + * @param typeKey name of the field to read and write type hints. Can be {@literal null} to disable type hints. + */ public DefaultMongoTypeMapper(@Nullable String typeKey) { - this(typeKey, Arrays.asList(new SimpleTypeInformationMapper())); + this(typeKey, Collections.singletonList(new SimpleTypeInformationMapper())); } + /** + * Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code typeKey}. + * + * @param typeKey name of the field to read and write type hints. Can be {@literal null} to disable type hints. + * @param mappingContext the mapping context. + */ public DefaultMongoTypeMapper(@Nullable String typeKey, MappingContext, ?> mappingContext) { this(typeKey, new DocumentTypeAliasAccessor(typeKey), mappingContext, - Arrays.asList(new SimpleTypeInformationMapper())); + Collections.singletonList(new SimpleTypeInformationMapper())); } + /** + * Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code typeKey}. Uses + * {@link UnaryOperator} to apply {@link CustomConversions}. + * + * @param typeKey name of the field to read and write type hints. Can be {@literal null} to disable type hints. + * @param mappingContext the mapping context to look up types using type hints. + * @see MappingMongoConverter#getWriteTarget(Class) + */ public DefaultMongoTypeMapper(@Nullable String typeKey, - MappingContext, ?> mappingContext, Function, Class> writeTarget) { + MappingContext, ?> mappingContext, UnaryOperator> writeTarget) { this(typeKey, new DocumentTypeAliasAccessor(typeKey), mappingContext, - Arrays.asList(new SimpleTypeInformationMapper())); + Collections.singletonList(new SimpleTypeInformationMapper())); this.writeTarget = writeTarget; } + /** + * Create a new {@link MongoTypeMapper} with fully-qualified type hints using {@code typeKey}. Uses + * {@link TypeInformationMapper} to map type hints. + * + * @param typeKey name of the field to read and write type hints. Can be {@literal null} to disable type hints. + * @param mappers + */ public DefaultMongoTypeMapper(@Nullable String typeKey, List mappers) { this(typeKey, new DocumentTypeAliasAccessor(typeKey), null, mappers); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index ea1dd87cd..0d0e7a685 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -34,6 +34,7 @@ import org.bson.conversions.Bson; import org.bson.types.ObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -122,7 +123,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App this.dbRefResolver = dbRefResolver; this.mappingContext = mappingContext; this.typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, mappingContext, - this::computeWriteTarget); + this::getWriteTarget); this.idMapper = new QueryMapper(this); this.spELContext = new SpELContext(DocumentPropertyAccessor.INSTANCE); @@ -672,7 +673,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App if (!property.isDbReference()) { if (property.hasExplicitWriteTarget()) { - return writeCollectionInternal(collection, new HijackedTypeInformation<>(property), new ArrayList<>()); + return writeCollectionInternal(collection, new TypeInformationWrapper<>(property), new ArrayList<>()); } return writeCollectionInternal(collection, property.getTypeInformation(), new BasicDBList()); } @@ -1608,6 +1609,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App * @param ref * @return */ + @Nullable Document readRef(DBRef ref) { return dbRefResolver.fetch(ref); } @@ -1630,7 +1632,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App * @return * @since 2.2 */ - protected Class computeWriteTarget(Class source) { + public Class getWriteTarget(Class source) { return conversions.getCustomWriteTarget(source).orElse(source); } @@ -1702,12 +1704,12 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } } - private static class HijackedTypeInformation implements TypeInformation { + private static class TypeInformationWrapper implements TypeInformation { private MongoPersistentProperty persistentProperty; private TypeInformation delegate; - public HijackedTypeInformation(MongoPersistentProperty property) { + public TypeInformationWrapper(MongoPersistentProperty property) { this.persistentProperty = property; this.delegate = property.getTypeInformation(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldType.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldType.java index 544d497c2..f550e8a58 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldType.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/FieldType.java @@ -31,8 +31,9 @@ import org.bson.types.ObjectId; *

* Bson types are identified by a {@code byte} {@link #getBsonType() value}. This enumeration typically returns the * according bson type value except for {@link #IMPLICIT} which is a marker to derive the field type from a property. - * + * * @author Mark Paluch + * @author Christoph Strobl * @since 2.2 * @see org.bson.BsonType */ @@ -67,7 +68,7 @@ public enum FieldType { /** * Returns the BSON type identifier. Can be {@code -1} if {@link FieldType} maps to a synthetic Bson type. - * + * * @return the BSON type identifier. Can be {@code -1} if {@link FieldType} maps to a synthetic Bson type. */ public int getBsonType() { @@ -76,7 +77,7 @@ public enum FieldType { /** * Returns the Java class used to represent the type. - * + * * @return the Java class used to represent the type. */ public Class getJavaClass() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java index 03b6fc27a..de77ee6b0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreatorUnitTests.java @@ -15,15 +15,17 @@ */ package org.springframework.data.mongodb.core; +import static org.springframework.data.mongodb.test.util.Assertions.*; + import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; -import org.assertj.core.api.Assertions; import org.bson.Document; import org.junit.Before; import org.junit.Test; + import org.springframework.data.annotation.Transient; import org.springframework.data.convert.WritingConverter; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -34,10 +36,12 @@ import org.springframework.data.mongodb.core.mapping.FieldType; import org.springframework.data.mongodb.core.mapping.MongoId; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.schema.MongoJsonSchema; -import org.springframework.lang.Nullable; /** + * Unit tests for {@link MappingMongoJsonSchemaCreator}. + * * @author Christoph Strobl + * @author Mark Paluch */ public class MappingMongoJsonSchemaCreatorUnitTests { @@ -58,52 +62,25 @@ public class MappingMongoJsonSchemaCreatorUnitTests { MongoJsonSchema schema = schemaCreator.createSchemaFor(VariousFieldTypes.class); - Assertions.assertThat(schema.toDocument().get("$jsonSchema", Document.class)) - .isEqualTo(Document.parse(VARIOUS_FIELD_TYPES)); - } - - @Test // DATAMONGO-1849 - public void requiredCtorArgs() { - - MongoJsonSchema schema = schemaCreator.createSchemaFor(RequiredArgsCtor.class); - Assertions.assertThat(schema.toDocument().get("$jsonSchema", Document.class)) - .isEqualTo(Document.parse(REQUIRED_ARGS_CTOR)); - } - - @Test // DATAMONGO-1849 - public void withNestedObject() { - - MongoJsonSchema schema = schemaCreator.createSchemaFor(WithNestedDomainType.class); - Assertions.assertThat(schema.toDocument().get("$jsonSchema", Document.class)) - .isEqualTo(Document.parse(WITH_NESTED_DOMAIN_TYPE)); - } - - @Test // DATAMONGO-1849 - public void withNestedThatHasRequiredFieldsObject() { - - MongoJsonSchema schema = schemaCreator.createSchemaFor(WithNestedDomainTypeHavingRequiredCtor.class); - - Assertions.assertThat(schema.toDocument().get("$jsonSchema", Document.class)) - .isEqualTo(Document.parse(WITH_NESTED_DOMAIN_TYPE_HAVING_REQUIRED_CTOR)); + assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo(Document.parse(VARIOUS_FIELD_TYPES)); } @Test // DATAMONGO-1849 public void withRemappedIdType() { MongoJsonSchema schema = schemaCreator.createSchemaFor(WithExplicitMongoIdTypeMapping.class); - Assertions.assertThat(schema.toDocument().get("$jsonSchema", Document.class)) - .isEqualTo(Document.parse(WITH_EXPLICIT_MONGO_ID_TYPE_MAPPING)); + assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo(WITH_EXPLICIT_MONGO_ID_TYPE_MAPPING); } @Test // DATAMONGO-1849 public void cyclic() { MongoJsonSchema schema = schemaCreator.createSchemaFor(Cyclic.class); - Assertions.assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo(Document.parse(CYCLIC)); + assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo(CYCLIC); } @Test // DATAMONGO-1849 - public void convterRegisterd() { + public void converterRegistered() { MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); MongoCustomConversions mcc = new MongoCustomConversions( @@ -114,8 +91,8 @@ public class MappingMongoJsonSchemaCreatorUnitTests { schemaCreator = new MappingMongoJsonSchemaCreator(converter); MongoJsonSchema schema = schemaCreator.createSchemaFor(WithNestedDomainType.class); - Assertions.assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo(Document.parse( - "{ 'type' : 'object', 'properties' : { '_id' : { 'type' : 'object' }, 'nested' : { 'type' : 'object' } } }")); + assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo( + "{ 'type' : 'object', 'properties' : { '_id' : { 'type' : 'object' }, 'nested' : { 'type' : 'object' } } }"); } // --> TYPES AND JSON @@ -171,32 +148,6 @@ public class MappingMongoJsonSchemaCreatorUnitTests { JustSomeEnum enumProperty; } - // --> REQUIRED ARGS (CTOR) - - static final String REQUIRED_ARGS_CTOR = "" + // - "{" + // - " 'type' : 'object'," + // - " 'required' : ['requiredCtorArg']," + // - " 'properties' : {" + // - " 'requiredCtorArg' : { 'type' : 'string' }," + // - " 'nullableCtorArg' : { 'type' : 'string' }," + // - " 'optionalArg' : { 'type' : 'string' }" + // - " }" + // - "}"; - - static class RequiredArgsCtor { - - String requiredCtorArg; - @Nullable String nullableCtorArg; - String optionalArg; - - public RequiredArgsCtor(String requiredCtorArg, @Nullable String nullableCtorArg) { - - this.requiredCtorArg = requiredCtorArg; - this.nullableCtorArg = nullableCtorArg; - } - } - // --> NESTED DOMAIN TYPE static final String WITH_NESTED_DOMAIN_TYPE = "" + // @@ -214,21 +165,6 @@ public class MappingMongoJsonSchemaCreatorUnitTests { VariousFieldTypes nested; } - static final String WITH_NESTED_DOMAIN_TYPE_HAVING_REQUIRED_CTOR = "" + // - "{" + // - " 'type' : 'object'," + // - " 'properties' : {" + // - " '_id' : { 'type' : 'object' }," + // - " 'nested' : " + REQUIRED_ARGS_CTOR + // - " }" + // - "}"; - - static class WithNestedDomainTypeHavingRequiredCtor { - - String id; - RequiredArgsCtor nested; - } - // --> EXPLICIT MONGO_ID MAPPING final String WITH_EXPLICIT_MONGO_ID_TYPE_MAPPING = "" + // diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index bff1c7aa1..56d296206 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -13,7 +13,7 @@ * Changed behavior of `Reactive/MongoOperations#count` now limiting the range to count matches within by passing on _offset_ & _limit_ to the server. * Kotlin extension methods accepting `KClass` are deprecated now in favor of `reified` methods. * Support of array filters in `Update` operations. -* <> from domain types. +* <> from domain types. [[new-features.2-1-0]] == What's New in Spring Data MongoDB 2.1 diff --git a/src/main/asciidoc/reference/mongo-custom-conversions.adoc b/src/main/asciidoc/reference/mongo-custom-conversions.adoc index 90d6be1aa..c823e6a81 100644 --- a/src/main/asciidoc/reference/mongo-custom-conversions.adoc +++ b/src/main/asciidoc/reference/mongo-custom-conversions.adoc @@ -2,7 +2,7 @@ == Custom Conversions - Overriding Default Mapping The most trivial way of influencing the the mapping result is by specifying the desired native MongoDB target type via the -`@Field` annotation. This allows to work with non mongoDB types like `BigDecimal` in the domain model while persisting +`@Field` annotation. This allows to work with non MongoDB types like `BigDecimal` in the domain model while persisting values in native `org.bson.types.Decimal128` format. .Explicit target type mapping diff --git a/src/main/asciidoc/reference/mongo-json-schema.adoc b/src/main/asciidoc/reference/mongo-json-schema.adoc index e53d63b3e..2f1bcf840 100644 --- a/src/main/asciidoc/reference/mongo-json-schema.adoc +++ b/src/main/asciidoc/reference/mongo-json-schema.adoc @@ -87,15 +87,14 @@ template.createCollection(Person.class, CollectionOptions.empty().schema(schema) ==== [[mongo.jsonSchema.generated]] -==== Generating the Schema +==== Generating a Schema -Setting up a schema can be a time consuming task and we encourage everyone who decides to do so, to really take the time -it takes. It's important, schema changes can be hard. However there might be times when one does not want to balked -with it, and that is where the `JsonSchemaCreator` comes into play. +Setting up a schema can be a time consuming task and we encourage everyone who decides to do so, to really take the time it takes. +It's important, schema changes can be hard. +However, there might be times when one does not want to balked with it, and that is where `JsonSchemaCreator` comes into play. -The `JsonSchemaCreator` and its default implementation generate the `MongoJsonSchema` out of the domain types metadata provided -by the mapping infrastructure. This means that <> as well as potential <> -are considered. +`JsonSchemaCreator` and its default implementation generates a `MongoJsonSchema` out of domain types metadata provided by the mapping infrastructure. +This means, that <> as well as potential <> are considered. .Generate Json Schema from domain type ==== @@ -103,25 +102,24 @@ are considered. ---- public class Person { - private final String firstname; <1> - private final @Nullable String lastname; <2> - private int age; <3> - private Species species; <4> - private Address address; <5> - private @Field(fieldType=SCRIPT) String theForce; <6> - private @Transient Boolean useTheForce; <7> + private final String firstname; <1> + private final int age; <2> + private Species species; <3> + private Address address; <4> + private @Field(fieldType=SCRIPT) String theForce; <5> + private @Transient Boolean useTheForce; <6> - public Person(String firstname, @Nullable String lastname) { <1> <2> + public Person(String firstname, int age) { <1> <2> this.firstname = firstname; - this.lastname = lastname; + this.age = age; } // gettter / setter omitted } -MongoJsonSchema schema = schemaCreator.jsonSchemaCreator(mongoOperations.getConverter()) - .createSchemaFor(DomainType.class); +MongoJsonSchema schema = MongoJsonSchemaCreator.create(mongoOperations.getConverter()) + .createSchemaFor(Person.class); template.createCollection(Person.class, CollectionOptions.empty().schema(schema)); ---- @@ -130,38 +128,36 @@ template.createCollection(Person.class, CollectionOptions.empty().schema(schema) ---- { 'type' : 'object', - 'required' : ['firstname', 'age'], <1> <3> + 'required' : ['age'], <2> 'properties' : { - 'firstname' : { 'type' : 'string' }, <1> - 'lastname' : { 'type' : 'string' }, <2> - 'age' : { 'bsonType' : 'int' } <3> - 'species' : { <4> + 'firstname' : { 'type' : 'string' }, <1> + 'age' : { 'bsonType' : 'int' } <2> + 'species' : { <3> 'type' : 'string', 'enum' : ['HUMAN', 'WOOKIE', 'UNKNOWN'] } - 'address' : { <5> + 'address' : { <4> 'type' : 'object' 'properties' : { 'postCode' : { 'type': 'string' } } }, - 'theForce' : { 'type' : 'javascript'} <6> + 'theForce' : { 'type' : 'javascript'} <5> } } ---- -<1> Required property as **not** `@Nullable` and used in the constructor. -<2> Optional property though used in the constructor it is still `@Nullable`. -<3> Primitive types are considered required properties. -<4> Enums are restricted to possible values. -<5> Object type properties get are inspected themselfes. -<6> `String` type property that is truned into `Code` by the mapping. -<7> `@Transient` properties are left out when generating the schema. +<1> Simple object properties are consideres regular properties. +<2> Primitive types are considered required properties +<3> Enums are restricted to possible values. +<4> Object type properties are inspected and represented as nested documents. +<5> `String` type property that is converted to `Code` by the converter. +<6> `@Transient` properties are omitted when generating the schema. ==== NOTE: `_id` properties using types that can be converted into `ObjectId` like `String` are mapped to `{ type : 'object' }` unless there is more specific information available via the `@MongoId` annotation. -[cols="3,1,6", options="header"] +[cols="2,2,6", options="header"] .Sepcial Schema Generation rules |=== | Java @@ -169,33 +165,33 @@ unless there is more specific information available via the `@MongoId` annotatio | Notes | `Object` -| type : object +| `type : object` | with `properties` if metadata available. | `Collection` -| type : array +| `type : array` | - | `Map` -| type : object +| `type : object` | - | `Enum` -| type : string +| `type : string` | with `enum` property holding the possible enumeration values. | `array` -| type : array +| `type : array` | simple type array unless it's a `byte[]` | `byte[]` -| bsonType : binData +| `bsonType : binData` | - |=== [[mongo.jsonSchema.query]] -==== Query a collection for matching Json Schema +==== Query a collection for matching JSON Schema You can use a schema to query any collection for documents that match a given structure defined by a JSON schema, as the following example shows: @@ -210,7 +206,7 @@ template.find(query(matchingDocumentStructure(schema)), Person.class); ==== [[mongo.jsonSchema.types]] -==== Json Schema Types +==== JSON Schema Types The following table shows the supported JSON schema types: