From ff999959a88ea3afd098e517ee4a05fcc26ccd94 Mon Sep 17 00:00:00 2001 From: Peter-Josef Meisch Date: Sun, 31 May 2020 22:59:32 +0200 Subject: [PATCH] DATAES-850 - Add warning and docs for missing TemporalAccessor configuration. Original PR: #472 (cherry picked from commit 859b22db8e396dc533d479dcf49a590c07b8dc24) --- .../elasticsearch-object-mapping.adoc | 3 ++ .../MappingElasticsearchConverter.java | 39 ++++++++++++++++++- ...SimpleElasticsearchPersistentProperty.java | 5 ++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/main/asciidoc/reference/elasticsearch-object-mapping.adoc b/src/main/asciidoc/reference/elasticsearch-object-mapping.adoc index 7e7496e7f..b1023af3f 100644 --- a/src/main/asciidoc/reference/elasticsearch-object-mapping.adoc +++ b/src/main/asciidoc/reference/elasticsearch-object-mapping.adoc @@ -48,6 +48,9 @@ The following annotations are available: ** `analyzer`, `searchAnalyzer`, `normalizer` for specifying custom custom analyzers and normalizer. * `@GeoPoint`: marks a field as _geo_point_ datatype. Can be omitted if the field is an instance of the `GeoPoint` class. +NOTE: Properties that derive from `TemporalAccessor` must either have a `@Field` annotation of type `FieldType.Date` or a custom converter must be registerd for this type. + +If you are using a custom date format, you need to use _uuuu_ for the year instead of _yyyy_. This is due to a https://www.elastic.co/guide/en/elasticsearch/reference/current/migrate-to-java-time.html#java-time-migration-incompatible-date-formats[change in Elasticsearch 7]. + The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic. [[elasticsearch.mapping.meta-model.rules]] diff --git a/src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java b/src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java index b33a8d272..a4605285a 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/convert/MappingElasticsearchConverter.java @@ -15,6 +15,7 @@ */ package org.springframework.data.elasticsearch.core.convert; +import java.time.temporal.TemporalAccessor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -26,7 +27,10 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; @@ -77,6 +81,8 @@ import org.springframework.util.ObjectUtils; public class MappingElasticsearchConverter implements ElasticsearchConverter, ApplicationContextAware, InitializingBean { + private static final Logger LOGGER = LoggerFactory.getLogger(MappingElasticsearchConverter.class); + private final MappingContext, ElasticsearchPersistentProperty> mappingContext; private final GenericConversionService conversionService; @@ -85,6 +91,8 @@ public class MappingElasticsearchConverter private ElasticsearchTypeMapper typeMapper; + private ConcurrentHashMap propertyWarnings = new ConcurrentHashMap<>(); + public MappingElasticsearchConverter( MappingContext, ElasticsearchPersistentProperty> mappingContext) { this(mappingContext, null); @@ -267,11 +275,26 @@ public class MappingElasticsearchConverter return null; } + Class rawType = targetType.getType(); + if (property.hasPropertyConverter() && String.class.isAssignableFrom(source.getClass())) { source = property.getPropertyConverter().read((String) source); + } else if (TemporalAccessor.class.isAssignableFrom(property.getType()) + && !conversions.hasCustomReadTarget(source.getClass(), rawType)) { + + // log at most 5 times + String propertyName = property.getOwner().getType().getSimpleName() + '.' + property.getName(); + String key = propertyName + "-read"; + int count = propertyWarnings.computeIfAbsent(key, k -> 0); + if (count < 5) { + LOGGER.warn( + "Type {} of property {} is a TemporalAccessor class but has neither a @Field annotation defining the date type nor a registered converter for reading!" + + " It cannot be mapped from a complex object in Elasticsearch!", + property.getType().getSimpleName(), propertyName); + propertyWarnings.put(key, count + 1); + } } - Class rawType = targetType.getType(); if (conversions.hasCustomReadTarget(source.getClass(), rawType)) { return rawType.cast(conversionService.convert(source, rawType)); } else if (source instanceof List) { @@ -473,6 +496,20 @@ public class MappingElasticsearchConverter if (property.hasPropertyConverter()) { ElasticsearchPersistentPropertyConverter propertyConverter = property.getPropertyConverter(); value = propertyConverter.write(value); + } else if (TemporalAccessor.class.isAssignableFrom(property.getType()) + && !conversions.hasCustomWriteTarget(value.getClass())) { + + // log at most 5 times + String propertyName = entity.getType().getSimpleName() + '.' + property.getName(); + String key = propertyName + "-write"; + int count = propertyWarnings.computeIfAbsent(key, k -> 0); + if (count < 5) { + LOGGER.warn( + "Type {} of property {} is a TemporalAccessor class but has neither a @Field annotation defining the date type nor a registered converter for writing!" + + " It will be mapped to a complex object in Elasticsearch!", + property.getType().getSimpleName(), propertyName); + propertyWarnings.put(key, count + 1); + } } if (!isSimpleType(value)) { diff --git a/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java b/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java index 15771b084..b834a46e1 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java @@ -122,8 +122,9 @@ public class SimpleElasticsearchPersistentProperty extends DateFormat dateFormat = field.format(); if (dateFormat == DateFormat.none) { - throw new MappingException(String.format("property %s is annotated with FieldType.%s but has no DateFormat defined", - getName(), field.type().name())); + throw new MappingException( + String.format("Property %s is annotated with FieldType.%s but has no DateFormat defined", + getOwner().getType().getSimpleName() + "." + getName(), field.type().name())); } ElasticsearchDateConverter converter = null;