Warm tip: This article is reproduced from stackoverflow.com, please click
elasticsearch spring spring-boot spring-data spring-data-elasticsearch

Spring Data Elasticsearch Repository query define date input parameter format

发布于 2020-04-05 00:23:47

I am using elasticsearch 6.5.3 and Spring Boot 2.1.6 and spring-data-elasticsearch 3.2.0.M1.

I have defined the Elasticsearch configuration as:

@Bean
    public ElasticsearchOperations elasticsearchTemplate() {
        return new ElasticsearchRestTemplate(client(), new CustomEntityMapper());
    }

    public static class CustomEntityMapper implements EntityMapper {

        private final ObjectMapper objectMapper;

        public CustomEntityMapper() {
            //we use this so that Elasticsearch understands LocalDate and LocalDateTime objects
            objectMapper = new ObjectMapper()
                              .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                              .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
                              .disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)
                              .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                              //MUST be registered BEFORE calling findAndRegisterModules
                              .registerModule(new JavaTimeModule())
                              .registerModule(new Jdk8Module());
            //only autodetect fields and ignore getters and setters for nonexistent fields when serializing/deserializing
            objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
                            .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
                            .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                            .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
                            .withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
            //load the other available modules as well
            objectMapper.findAndRegisterModules();
        }

        @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);
        }
    }

I have a repository with a method defined as:

List<AccountDateRollSchedule> findAllByNextRollDateTimeLessThanEqual(final LocalDateTime dateTime);

And the POJO AccountDateRollSchedule defines that field as:

    @Field(type = FieldType.Date, format = DateFormat.date_hour_minute)
    @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm")
    private LocalDateTime nextRollDateTime;

I see my index properly has that field created as declared and expected:

"nextRollDateTime": {
          "type": "date",
          "format": "date_hour_minute"
        }

Also querying the index returns the field formatted as expected:

"nextRollDateTime" : "2019-06-27T13:34"

My repository query would translate to:

{"query": 
  {"bool" : 
    {"must" : 
      {"range" : 
        {"nextRollDateTime" : 
          {"from" : null, 
           "to" : "?0", 
           "include_lower" : true,
           "include_upper" : true
          }
        }
      }
    }
  }
}

But passing any LocalDateTime input to the method does NOT respect the format defined for the field, the FULL format is always used instead. Invoking:

findAllByNextRollDateTimeLessThanEqual(LocalDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.MINUTES));

gives me the following exception (any @DateTimeFormat or @JsonFormat annotation on the method parameter in the repository is ignored):

Unrecognized chars at the end of [2019-07-22T09:07:00.000]: [:00.000]

If I instead change the repository method to accept a String and pass a String formatted exactly as expected as input to it, it works no problem.

Is it possible to somehow define the format used for the date parameter passed in input to the repository method or have Spring use the one configured on the field itself?

I would like not to wrap that method for a simple conversion like this (I did and it works), and I would also like to avoid using long type for the date field

Thanks and cheers

For reference, I also open issue on Spring JIRA

Questioner
grog
Viewed
154
P.J.Meisch 2020-01-31 22:59

These problems are one reason why we move away from using and exposing the JacksonMapper in Spring Data Elasticsearch. From version 4.0 on all you need on your property is the one annotation:

@Field(type = FieldType.Date, format = DateFormat.date_hour_minute)
private LocalDateTime nextRollDateTime;

This will then be used in writing the index mappings, when entities are indexed and retrieved, and also when repository method and queries are processed.

But for the 3.2.x version you will have to use a workaround like the wrapping you mentioned.