Spring Data Elasticsearch中如何为TemporalAccessor类型HashMap指定Field类型
Spring Data Elasticsearch 配置
Map<String, ZonedDateTime>字段类型的方案 直接在Map<String, ZonedDateTime>类型的字段上加@Field(FieldType.Date)注解时,Spring Data Elasticsearch会将整个Map容器识别为日期类型尝试解析,无法正确识别Map value的日期类型;如果不加类型配置则会抛出如下错误:
Type .. of property .. is a TemporalAccessor class but has neither a @Field annotation defining the date type nor a registered converter for writing! It will be mapped to a complex object in Elasticsearch!
中文翻译:属性xxx的类型属于TemporalAccessor类,但既没有定义日期类型的@Field注解,也没有注册可用于写入的转换器!它将被映射为Elasticsearch中的复杂对象!
解决方案1:自定义类型转换器(推荐,无额外结构层级)
通过自定义转换器实现Map类型的序列化和反序列化,步骤如下:
- 实现写入转换器,将
Map<String, ZonedDateTime>序列化为ES可识别的Map<String, String>,值为标准日期格式字符串:
import org.springframework.core.convert.converter.Converter; import java.time.ZonedDateTime; import java.util.Map; import java.util.stream.Collectors; public class ZonedDateTimeMapWriteConverter implements Converter<Map<String, ZonedDateTime>, Map<String, String>> { @Override public Map<String, String> convert(Map<String, ZonedDateTime> source) { return source.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, // 可通过DateTimeFormatter自定义需要的日期格式 entry -> entry.getValue().toString() )); } }
- 实现读取转换器,将ES返回的
Map<String, String>反序列化为Map<String, ZonedDateTime>:
import org.springframework.core.convert.converter.Converter; import java.time.ZonedDateTime; import java.util.Map; import java.util.stream.Collectors; public class ZonedDateTimeMapReadConverter implements Converter<Map<String, String>, Map<String, ZonedDateTime>> { @Override public Map<String, ZonedDateTime> convert(Map<String, String> source) { return source.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, // 和写入时的格式对应解析 entry -> ZonedDateTime.parse(entry.getValue()) )); } }
- 注册转换器到Spring Data Elasticsearch配置中:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport; import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions; import java.util.List; @Configuration public class ElasticsearchConfig extends ElasticsearchConfigurationSupport { @Bean @Override public ElasticsearchCustomConversions elasticsearchCustomConversions() { return new ElasticsearchCustomConversions(List.of( new ZonedDateTimeMapWriteConverter(), new ZonedDateTimeMapReadConverter() )); } }
- 字段上标注类型为Object即可:
@Field(type = FieldType.Object) private Map<String, ZonedDateTime> timePoints = new HashMap<>();
解决方案2:封装自定义嵌套类
如果不想写转换器,可以将ZonedDateTime封装为内部类,给内部类的日期字段加日期类型注解:
// 自定义封装类 static class TimePoint { @Field(type = FieldType.Date, format = DateFormat.ISO_DATE_TIME) private ZonedDateTime time; // getter、setter省略 } // 字段修改为 private Map<String, TimePoint> timePoints = new HashMap<>();
该方案的缺点是ES中存储的结构会多一层time字段,查询和返回数据时需要额外处理层级。
内容的提问来源于stack exchange,提问作者Alex1
相关产品推荐
相关产品推荐

