如何在Spring Data Elasticsearch中为时态访问器的HashMap提供字段类型



给定

private Map<String, ZonedDateTime> timePoints = new HashMap<>();

如何提示春季数据字段的类型?

当直接放在字典上时,转换器会尝试将键和值一起解析,如果它们是日期字符串的话。

@Field(FieldType.Date)
private Map<String, ZonedDateTime> timePoints = new HashMap<>();

当没有提供字段类型时,会出现以下错误:

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!

从文档中,您可能需要创建自定义转换器,并将它们添加到自定义ElasticsearchCustomConversionsbean中。以下转换器似乎可以工作:

@Bean
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
return new ElasticsearchCustomConversions(
Arrays.asList(new ZonedDateTimeWriteConverter(), new ZonedDateTimeReadConverter()));
}

@WritingConverter
static class ZonedDateTimeWriteConverter implements Converter<ZonedDateTime, String> {
@Override
public String convert(ZonedDateTime source) {
return DateTimeFormatter.ISO_ZONED_DATE_TIME.format(source);
}
}
@ReadingConverter
static class ZonedDateTimeReadConverter implements Converter<String, ZonedDateTime> {
@Override
public ZonedDateTime convert(String source) {
return ZonedDateTime.from(DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(source));
}
}

像一样在属性上放置注释

@Field(FieldType.Date)
private Map<String, ZonedDateTime> timePoints = new HashMap<>();

无法工作,因为Map不是临时类型,因此无法转换为临时类型。

如果保留注释,则Map<String, ZonedDateTime>将被解释为对象。例如,如果你有

Map<String, ZonedDateTime> map = new Map();
map.put("utc", ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("UTC")));
map.put("paris", ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("Europe/Paris")));

然后在用Spring Data Elasticsearch存储这个对象时,它将尝试创建一个要发送到Elasticsearch(JSON表示)的对象,如下所示:

{
"utc": {

},
"paris": {

}
}

应该表示时态的内部对象存储为嵌套对象,而不是某个转换值,因为无法将字段类型添加到映射的值中-您可以在日志中看到有关的警告。

但是在Elasticsearch中使用Map作为属性无论如何都是有问题的。键被解释为子对象的属性。之前不可能在索引中定义类型的映射,因为不知道这些属性可以有什么可能的名称。在我的例子中;utc";以及";巴黎";,但它可以是任何字符串。每一个值将由Elasticsearch作为动态映射字段添加到索引中。这可能会导致所谓的映射爆炸,因此Elasticsearch将索引中的字段数限制为默认值1000。您可以重新考虑在Elasticsearch中存储数据的方式。

如果你想坚持使用Map,你需要编写一个自定义转换器,能够将你的Map<String, ZonedDateTime>转换为Map<String, String>并返回。

不知道我是否100%理解您的问题,但您应该尝试实现Persistable并添加带注释的属性,这些值将自动为存储在Elasticsearch中的实体维护,例如:

@Document(indexName = "person")
public class Person implements Persistable<Long> {
@Nullable @Id
private Long id;
@CreatedDate
@Nullable @Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private Instant created;
@Nullable @CreatedBy
private String createdBy;
@LastModifiedDate
@Nullable @Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private Instant lastModified;
@Nullable @LastModifiedBy
private String lastModifiedBy;
@Nullable
public Long getId() { return id; }
@Override
public boolean isNew() { return id == null || (createdBy == null && created == null); }
// other properties, getter, setter...
}

最新更新