Using Instant, LocalDateTime and ZonedDateTime with Spring Boot and ElasticSearch Using Instant, LocalDateTime and ZonedDateTime with Spring Boot and ElasticSearch elasticsearch elasticsearch

Using Instant, LocalDateTime and ZonedDateTime with Spring Boot and ElasticSearch


Managed to get it to work with Spring Boot 2.1.4 and Spring Data Jest. Here is what I did:

  1. Example domain object:

    @Document(indexName = "datetest")public class DateTest {    @Id    private String id;    @Field(type = FieldType.Date, 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", timezone = "UTC")    private Instant instant = Instant.now();    @Field(type = FieldType.Date, 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 ZonedDateTime zonedDateTime = ZonedDateTime.now();    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS")    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd'T'HH:mm:ss.SSS")    private LocalDateTime localDateTime = LocalDateTime.now();    // getters/setters}
  2. The ElasticSearch/JEST config:

    @Configurationpublic class ESConfig {    @Bean    public EntityMapper getEntityMapper() {        return new CustomEntityMapper();    }    @Bean    @Primary    public ElasticsearchOperations elasticsearchTemplate(final JestClient jestClient,            final ElasticsearchConverter elasticsearchConverter,            final SimpleElasticsearchMappingContext simpleElasticsearchMappingContext, EntityMapper mapper) {        return new JestElasticsearchTemplate(jestClient, elasticsearchConverter,                new DefaultJestResultsMapper(simpleElasticsearchMappingContext, mapper));    }    public 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);        }    }}
  3. The results in ElasticSearch:

    Screenshot of results in ElasticSearch

Hope this helps.


That's because spring-data-jest uses DefaultEntityMapper (part of Spring Data), which creates its own ObjectMapper and doesn't use the one provided by Spring boot. This can be seen in this related question.

You're on the right track with your solution by defining your own EntityMapper, for example CustomEntityMapper. However, spring-data-jest wraps this mapper into a class called DefaultJestResultsMapper, which is then used by a bean called JestElasticsearchTemplate.

So, probably you should do something like this:

@Beanpublic JestResultsMapper resultMapper(CustomEntityMapper entityMapper) {    return new DefaultJestResultsMapper(entityMapper);}@Beanpublic JestElasticSearchTemplate template(JestClient client, JestResultsMapper resultsMapper) {    return new JestElasticSearchTemplate(client, resultsMapper);}

This should inject your CustomEntityMapper into a JestResultsMapper, which is in turn injected into JestElasticSearchTemplate used by the framework.

Within CustomEntityMapper you can either autowire the default ObjectMapper (which will automatically add the JavaTimeModule) or you can configure one on your own.


According to this answer from version 2 of Spring Boot,it should work out of the box as you want in terms of producing string from java.time objects

If you have

com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0

as dependency and below line in application.properties

spring.jackson.serialization.write_dates_as_timestamps=false

So only thing left would be to add timezone notation to default string which won't have it.

If standard formatters won't work for you may always write your own serialiser/deserialiser and attach it like explained here