如何在 Spring Data Elasticsearch 中为临时访问器的 HashMap 提供字段类型
How to provide Field type for HashMap of temporal accessors in Spring Data Elasticsearch
给出
private Map<String, ZonedDateTime> timePoints = new HashMap<>();
如何提示spring-data字段的类型?
当直接放在字典中时,如果键和值是日期字符串,转换器会尝试将它们一起解析。
@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!
不知道我是否 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...
}
从 documentation 看来,您可能必须创建自定义转换器并将它们添加到自定义 ElasticsearchCustomConversions
bean。以下转换器似乎有效:
@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”和“paris”,但它可以是任何字符串。这些中的每一个
Elasticsearch 会将值作为动态映射字段添加到索引中。这可能会导致称为 mapping explosion, therefore Elasticsearch limits the number of fields in an index to a default value of 1000 的东西。您可以重新考虑在 Elasticsearch 中存储数据的方式。
如果您想坚持使用 Map
,您需要编写一个自定义转换器,能够将您的 Map<String, ZonedDateTime>
转换为 Map<String, String>
并返回。
给出
private Map<String, ZonedDateTime> timePoints = new HashMap<>();
如何提示spring-data字段的类型?
当直接放在字典中时,如果键和值是日期字符串,转换器会尝试将它们一起解析。
@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!
不知道我是否 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...
}
从 documentation 看来,您可能必须创建自定义转换器并将它们添加到自定义 ElasticsearchCustomConversions
bean。以下转换器似乎有效:
@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”和“paris”,但它可以是任何字符串。这些中的每一个
Elasticsearch 会将值作为动态映射字段添加到索引中。这可能会导致称为 mapping explosion, therefore Elasticsearch limits the number of fields in an index to a default value of 1000 的东西。您可以重新考虑在 Elasticsearch 中存储数据的方式。
如果您想坚持使用 Map
,您需要编写一个自定义转换器,能够将您的 Map<String, ZonedDateTime>
转换为 Map<String, String>
并返回。