diff --git a/src/main/asciidoc/reference/elasticsearch-object-mapping.adoc b/src/main/asciidoc/reference/elasticsearch-object-mapping.adoc index b150cde1d..7e7496e7f 100644 --- a/src/main/asciidoc/reference/elasticsearch-object-mapping.adoc +++ b/src/main/asciidoc/reference/elasticsearch-object-mapping.adoc @@ -43,7 +43,7 @@ The following annotations are available: * `@Field`: Applied at the field level and defines properties of the field, most of the attributes map to the respective https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html[Elasticsearch Mapping] definitions (the following list is not complete, check the annotation Javadoc for a complete reference): ** `name`: The name of the field as it will be represented in the Elasticsearch document, if not set, the Java field name is used. ** `type`: the field type, can be one of _Text, Keyword, Long, Integer, Short, Byte, Double, Float, Half_Float, Scaled_Float, Date, Date_Nanos, Boolean, Binary, Integer_Range, Float_Range, Long_Range, Double_Range, Date_Range, Ip_Range, Object, Nested, Ip, TokenCount, Percolator, Flattened, Search_As_You_Type_. See https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html[Elasticsearch Mapping Types] -** `format` and `pattern` custom definitions for the _Date_ type. +** `format` and `pattern` definitions for the _Date_ type. `format` must be defined for date types. ** `store`: Flag wether the original field value should be store in Elasticsearch, default value is _false_. ** `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. 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 beab6b433..15771b084 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 @@ -117,9 +117,15 @@ public class SimpleElasticsearchPersistentProperty extends boolean isTemporalAccessor = TemporalAccessor.class.isAssignableFrom(getType()); boolean isDate = Date.class.isAssignableFrom(getType()); - if (field != null && field.type() == FieldType.Date && (isTemporalAccessor || isDate)) { + if (field != null && (field.type() == FieldType.Date || field.type() == FieldType.Date_Nanos) + && (isTemporalAccessor || isDate)) { 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())); + } + ElasticsearchDateConverter converter = null; if (dateFormat == DateFormat.custom) { diff --git a/src/test/java/org/springframework/data/elasticsearch/core/LogEntityTests.java b/src/test/java/org/springframework/data/elasticsearch/core/LogEntityTests.java index b2b468e8d..0a9a52cca 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/LogEntityTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/LogEntityTests.java @@ -34,6 +34,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.dao.DataAccessException; import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.DateFormat; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; @@ -146,7 +147,7 @@ public class LogEntityTests { @Field(type = Ip) private String ip; - @Field(type = Date) private java.util.Date date; + @Field(type = Date, format = DateFormat.date_time) private java.util.Date date; private LogEntity() {} diff --git a/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderTests.java b/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderTests.java index 7b4c89934..4a70c8785 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderTests.java @@ -859,7 +859,7 @@ public class MappingBuilderTests extends MappingContextBaseTests { @Nullable @Id private String id; - @Nullable @Field(type = FieldType.Date, index = false) private Date createdDate; + @Nullable @Field(type = FieldType.Date, format = DateFormat.date_time, index = false) private Date createdDate; @Nullable public String getId() { diff --git a/src/test/java/org/springframework/data/elasticsearch/core/index/SimpleElasticsearchDateMappingTests.java b/src/test/java/org/springframework/data/elasticsearch/core/index/SimpleElasticsearchDateMappingTests.java index eec5b4c5c..ff4a34fb9 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/index/SimpleElasticsearchDateMappingTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/index/SimpleElasticsearchDateMappingTests.java @@ -41,10 +41,10 @@ public class SimpleElasticsearchDateMappingTests extends MappingContextBaseTests private static final String EXPECTED_MAPPING = "{\"properties\":{\"message\":{\"store\":true," + "\"type\":\"text\",\"index\":false,\"analyzer\":\"standard\"},\"customFormatDate\":{\"type\":\"date\",\"format\":\"dd.MM.uuuu hh:mm\"}," - + "\"defaultFormatDate\":{\"type\":\"date\"},\"basicFormatDate\":{\"" + + "\"basicFormatDate\":{\"" + "type\":\"date\",\"format\":\"basic_date\"}}}"; - @Test // DATAES-568 + @Test // DATAES-568, DATAES-828 public void testCorrectDateMappings() { String mapping = getMappingBuilder().buildPropertyMapping(SampleDateMappingEntity.class); @@ -67,8 +67,6 @@ public class SimpleElasticsearchDateMappingTests extends MappingContextBaseTests @Field(type = Date, format = DateFormat.custom, pattern = "dd.MM.uuuu hh:mm") private LocalDateTime customFormatDate; - @Field(type = FieldType.Date) private LocalDateTime defaultFormatDate; - @Field(type = FieldType.Date, format = DateFormat.basic_date) private LocalDateTime basicFormatDate; } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentPropertyUnitTests.java b/src/test/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentPropertyUnitTests.java index 182fb75cd..3d6dec9e0 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentPropertyUnitTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentPropertyUnitTests.java @@ -173,6 +173,20 @@ public class SimpleElasticsearchPersistentPropertyUnitTests { assertThat(seqNoProperty.isReadable()).isFalse(); } + @Test // DATAES-828 + void shouldRequireFormatForDateField() { + assertThatExceptionOfType(MappingException.class) // + .isThrownBy(() -> context.getRequiredPersistentEntity(DateFieldWithNoFormat.class)) // + .withMessageContaining("date"); + } + + @Test // DATAES-828 + void shouldRequireFormatForDateNanosField() { + assertThatExceptionOfType(MappingException.class) // + .isThrownBy(() -> context.getRequiredPersistentEntity(DateNanosFieldWithNoFormat.class)) // + .withMessageContaining("date"); + } + static class InvalidScoreProperty { @Nullable @Score String scoreProperty; } @@ -195,4 +209,12 @@ public class SimpleElasticsearchPersistentPropertyUnitTests { SeqNoPrimaryTerm seqNoPrimaryTerm; String string; } + + static class DateFieldWithNoFormat { + @Field(type = FieldType.Date) LocalDateTime datetime; + } + + static class DateNanosFieldWithNoFormat { + @Field(type = FieldType.Date_Nanos) LocalDateTime datetime; + } }