14

I am using Spring Data support for Elasticsearch. Here is the timestamp field mapping:

@Field(type = FieldType.Date, index = FieldIndex.not_analyzed, store = true,
        format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
private LocalDateTime timestamp;

This results in mapping of the field in Elasticsearch as follows:

"timestamp":{"type":"date","store":true,"format":"yyyy-MM-dd'T'HH:mm:ss.SSSZZ"}

When I use java.util.Date instead everything works fine. However, when I switch to java.time.LocalDateTime as above the document sent to Elasticsearch causes an exception. Here is the document (timestamp field only for brevity):

"timestamp": {
    "hour":7, "minute":56, "second":9, "nano":147000000, "year":2017, "month":"FEBRUARY",
    "dayOfMonth":13, "dayOfWeek":"MONDAY", "dayOfYear":44, "monthValue":2, "chronology": {
        "id":"ISO", "calendarType": "iso8601"
    }
}

And the exception:

MapperParsingException[failed to parse [timestamp]]; nested: IllegalArgumentException[unknown property [hour]];
(...)
Caused by: java.lang.IllegalArgumentException: unknown property [hour]

It looks like the pattern is being ignored here when jsonizing the document. Any possible tips? Or perhaps you might know how to use the "built-in" _timestamp field with Spring Data?

theadam
  • 3,961
  • 4
  • 25
  • 41

3 Answers3

12

Check https://github.com/spring-projects/spring-data-elasticsearch/wiki/Custom-ObjectMapper to add JavaTimeModule to your ObjectMapper.

@Configuration
public class ElasticSearchConfiguration {

  @Bean
  public ElasticsearchTemplate elasticsearchTemplate(Client client) {
    return new ElasticsearchTemplate(client, new CustomEntityMapper());
  }

  public static class CustomEntityMapper implements EntityMapper {

    private final ObjectMapper objectMapper;

    public CustomEntityMapper() {
      objectMapper = new ObjectMapper();
      objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
      objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
      objectMapper.registerModule(new CustomGeoModule());
      objectMapper.registerModule(new JavaTimeModule());
    }

    @Override
    public String mapToString(Object object) throws IOException {
      return objectMapper.writeValueAsString(object);
    }

    @Override
    public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
      return objectMapper.readValue(source, clazz);
    }
  }
}
Cedric Thiebault
  • 1,015
  • 1
  • 15
  • 28
8

I was experiencing the similar issue: 'Z' in the date value is treated as a character, so the date parse failed. My solution is using custom pattern to make sure the conversion correct:

@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
private LocalDateTime dateField;

And if we don't want to repeat the pattern on different fields again and again, we can try to centralize to the conversion logic. Spring Data Elastic Search provide the custom conversion feature, check here for the example.

Basically we can write a converter from String to LocalDateTime, and put the date pattern over there.

Eric Tan
  • 1,377
  • 15
  • 14
  • This https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.mapping.meta-model.conversions is the best solution provided already by Spring Data. – Romeo Jr Maranan May 20 '20 at 02:42
0

This works for me

    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    private LocalDateTime createdAt;
Yi Zhang
  • 325
  • 2
  • 4