我正在使用SpringBoot 2.2.5和Elasticsearch 6.8.6。我正在从Spring Data Jest迁移到通过ElasticsearchEntityMapper
使用Spring Data Elasticsearch REST传输机制。
我有一个Date
字段,定义如下:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private Date date;
我想要这样存储在Elasticsearch中的日期:
"date": "2020-04-02T14:49:05.672+0000"
启动应用程序时,将创建索引,但是当我尝试保存实体时,出现以下异常:
Caused by: org.elasticsearch.client.ResponseException: method [POST], host [http://localhost:9200], URI [/trends/estrend?timeout=1m], status line [HTTP/1.1 400 Bad Request]
{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"failed to parse field [date] of type [date] in document with id 'rS5UP3EB9eKtCTMXW_Ky'"}],"type":"mapper_parsing_exception","reason":"failed to parse field [date] of type [date] in document with id 'rS5UP3EB9eKtCTMXW_Ky'","caused_by":{"type":"illegal_argument_exception","reason":"Invalid format: \"1585905425266\" is malformed at \"5266\""}},"status":400}
关于我做错了什么以及应该如何解决的任何指针?
下面的配置和实体定义:
@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
@Value("${spring.data.elasticsearch.host}")
private String elasticSearchHost;
@Value("${spring.data.elasticsearch.port}")
private String elasticSearchPort;
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(elasticSearchHost + ":" + elasticSearchPort)
.usingSsl()
.build();
return RestClients.create(clientConfiguration).rest();
}
@Bean
public EntityMapper entityMapper() {
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(), new DefaultConversionService());
entityMapper.setConversions(elasticsearchCustomConversions());
return entityMapper;
}
}
package com.es.test;
import java.util.Date;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Document(indexName = "trends")
public class EsTrend {
@Id
private UUID id;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private Date date;
private String entityOrRelationshipId;
// getter and setters
}
更新:
如果禁用ElasticsearchEntityMapper
bean,则不会收到异常,并且日期会以正确的格式写入Elasticsearch。我还需要为ElasticsearchEntityMapper
配置什么吗?
首先,请不要使用基于Jackson的默认映射器。在Spring Data Elasticsearch的下一个主要版本(4.0)中将其删除。然后将没有可用的选择,并且在内部使用ElasticsearchEntityMapper
。
关于您的问题:Spring Boot当前使用的3.2版中的ElasticsearchEntityMapper
并不使用@Field
属性中的日期相关信息来转换实体,它仅用于创建索引映射。这是一项缺少的功能或错误,已在下一个主要版本中修复,整个映射过程已在此进行了全面检查。
您在当前情况下可以做什么:您需要添加自定义转换器。您可以在配置类中执行以下操作:
@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
@Value("${spring.data.elasticsearch.host}")
private String elasticSearchHost;
@Value("${spring.data.elasticsearch.port}")
private String elasticSearchPort;
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(elasticSearchHost + ":" + elasticSearchPort)
.usingSsl()
.build();
return RestClients.create(clientConfiguration).rest();
}
@Bean
public EntityMapper entityMapper() {
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(), new DefaultConversionService());
entityMapper.setConversions(elasticsearchCustomConversions());
return entityMapper;
}
@Override
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
return new ElasticsearchCustomConversions(Arrays.asList(DateToStringConverter.INSTANCE, StringToDateConverter.INSTANCE));
}
enum DateToStringConverter implements Converter<Date, String> {
INSTANCE;
@Override
public String convert(Date date) {
return formatter.format(date);
}
}
enum StringToDateConverter implements Converter<String, Date> {
INSTANCE;
@Override
public Date convert(String s) {
try {
return formatter.parse(s);
} catch (ParseException e) {
return null;
}
}
}
}
但是您仍然需要在@Field
注释中使用dateformat,因为创建正确的索引映射需要使用它。
并且您应该更改代码以使用Java 8引入的时间类,例如LocalDate
或LocalDateTime
,Spring Data Elasticsearch开箱即用地支持这些,而java.util.Date
需要自定义转换器。