elasticsearch - 如何使用 Spring Data Elasticsearch 正确格式化日期
问题描述
我正在使用 SpringBoot 2.2.5 和 Elasticsearch 6.8.6。我正在从 Spring Data Jest 迁移到使用 Spring Data Elasticsearch REST 传输机制和ElasticsearchEntityMapper
.
我有一个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
使用了。
至于你的问题:ElasticsearchEntityMapper
目前Spring Boot使用的3.2版本不使用@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));
}
@WritingConverter
enum DateToStringConverter implements Converter<Date, String> {
INSTANCE;
@Override
public String convert(Date date) {
return formatter.format(date);
}
}
@ReadingConverter
enum StringToDateConverter implements Converter<String, Date> {
INSTANCE;
@Override
public Date convert(String s) {
try {
return formatter.parse(s);
} catch (ParseException e) {
return null;
}
}
}
}
但是,您仍然需要在@Field
注释中包含日期格式,因为需要它来创建正确的索引映射。
您应该更改您的代码以使用 Java 8 引入的时间类,如LocalDate
or LocalDateTime
,Spring Data Elasticsearch 开箱即用地支持这些,而java.util.Date
需要自定义转换器。
编辑 09.04.2020:添加了必要的@WritingConverter
注释 @ReadingConverter
。
编辑 19.04.2020: Spring Data Elasticsearch 4.0 也将支持带有注释java.util.Date
的开箱即用类。@Field
推荐阅读
- python - 如何在 python 中打开 URL 1 分钟并在 1 分钟后关闭?
- ios - 每个设备的 Swift 布局
- typescript - 如何实现带可变键的接口并同时定义键
- microservices - vertx multi verticle在单个微服务中的用例是什么?
- android - Android apk 未在 Quasar 中使用 cordova 更新
- amazon-web-services - 在 Beanstalk 上运行的单个 Docker 容器中找不到 DNS
- java - JDB(Java 调试器)命令不接受类路径选项中的空格
- javascript - 如何在Javascript中每n秒创建一个实例?
- javascript - React 设置可拖动的 antd modal
- vb.net - 在最新的 Visual Studio 16.6.2 中,我的 Windows 菜单中缺少“新窗口”命令