Elasticsearch @Query查询时LocalDateTime秒数丢失格式化异常问题
问题原因
你遇到的问题是Spring Data Elasticsearch对@Query注解中的LocalDateTime参数序列化时,默认Jackson序列化规则会对秒值为0的LocalDateTime自动省略秒部分,导致最终生成的查询语句中日期格式为yyyy-MM-dd'T'HH:mm,和你在@Field中定义的yyyy-MM-dd'T'HH:mm:ss格式不匹配,ES无法解析从而报错。当秒值非0时序列化会保留秒部分,所以查询能正常执行。
解决方案
你可以选择以下任意一种方案解决:
方案1:扩展ES字段支持的日期格式
修改字段的@Field注解配置,给ES日期字段添加兼容不带秒的格式,ES支持通过||分隔配置多个可解析的日期格式:@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm") private LocalDateTime startTime; @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm") private LocalDateTime endTime;注意修改后需要重建索引让新的映射规则生效。
方案2:强制LocalDateTime序列化始终携带秒
自定义全局Jackson序列化规则,配置LocalDateTime的序列化器强制输出完整的yyyy-MM-dd'T'HH:mm:ss格式,哪怕秒值为0:@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))); mapper.registerModule(javaTimeModule); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return mapper; } }方案3:业务层自行格式化日期参数
将Repository方法的参数类型改为String,业务层调用前先自行把LocalDateTime格式化为符合要求的字符串再传入,避免框架自动序列化导致格式异常:// Repository层修改 @Query("{" + " \"nested\": {" + " \"path\" : \"panel.channels\"," + " \"query\" : {" + " \"bool\" : {" + " \"must\" : [" + " { \"range\" : { \"panel.channels.startTime\" : { \"lte\": \"?1\" } } }," + " { \"range\" : { \"panel.channels.endTime\" : { \"gte\": \"?0\" } } } " + " ]" + " }" + " }" + " }" + "}") Flux<Document> search(String from, String to); // 业务层调用示例 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); String fromStr = from.format(formatter); String toStr = to.format(formatter); repository.search(fromStr, toStr);方案4:改用API构建查询替代原生@Query
不用手写JSON的@Query,改用Spring Data Elasticsearch提供的Criteria查询、NativeSearchQuery等API构建查询语句,框架会自动读取@Field中定义的日期格式处理参数,不会出现格式不匹配问题。
内容的提问来源于stack exchange,提问作者Raphaël Lemaire

