最近壹哥的一个学生,在利用spring-data-elasticsearch访问ElasticSearch(ES)时,遇到了一个关于日期类型的BUG,困扰了很久。然后他就找壹哥给他解决,接下来壹哥就把解决的过程给大家复现一下,希望本文可以给遇到同样问题的同学一点启发。
一. 问题复现
1. 原始代码
我们先来看看他的POM.xml文件配置,如下所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
实体类的代码如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "user_info",shards = 1,replicas = 1,createIndex = false)
public class UserInfo {
@Id
@Field(type = FieldType.Long)
private Long id;
@Field(type = FieldType.Keyword)
private String name;
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String remark;
@Field(type = FieldType.Date,format = DateFormat.date_time)
private Date jobday;
}
创建索引的单元测试类代码如下:
@Test
void createIndex() {
// 创建索引
elasticsearchRestTemplate.createIndex(UserInfo.class);
// 声明索引中 mapping部分
elasticsearchRestTemplate.putMapping(UserInfo.class);
}
以下是ES中创建的索引 Mapping结构,其中日期类型的format为date_time。
看了以上代码,你能猜出来哪里有问题吗?其实这个bug是发生在写入操作中,代码如下:
@Test
void save() {
final UserInfo userInfo = new UserInfo();
userInfo.setId(100l);
userInfo.setName("张三");
userInfo.setJobday(new Date());
userInfo.setRemark("张三是法外狂徒");
userRepository.save(userInfo);
}
2. 异常信息
我们来看看产生的异常信息。
Caused by: ElasticsearchException[Elasticsearch exception [type=illegal_argument_exception, reason=Invalid format: "1669366585010" is malformed at "5010"]]
at org.elasticsearch.ElasticsearchException.innerFromXContent(ElasticsearchException.java:509)
at org.elasticsearch.ElasticsearchException.fromXContent(ElasticsearchException.java:420)
at org.elasticsearch.ElasticsearchException.innerFromXContent(ElasticsearchException.java:450)
at org.elasticsearch.ElasticsearchException.failureFromXContent(ElasticsearchException.java:616)
at org.elasticsearch.rest.BytesRestResponse.errorFromXContent(BytesRestResponse.java:169)
... 87 more
通过查看报错信息,我们可以发现该bug是由于写入请求时,给定的日期格式与mapping中限定的date_time格式不一致导致的!
二. 异常原因分析
既然是时间格式有问题,那么我们就需要知道写请求中的日期格式,与ES mapping中的date_time格式分别是什么。
从日志中我们可以看到,写入请求中的日期格式为 1669366585010,即long型整数的时间戳格式。根据ES的官方文档,我们可以看到date_time的格式要求如下:
我们可以可以看到,ES里的格式默认为: yyyy-MM-dd'T'HH:mm:ss.SSSZ
这样bug产生的原因就很清晰了,该学生写入请求中的是 long,而ES索引中要求的是 yyyy-MM-dd'T'HH:mm:ss.SSSZ,这两个不一致,自然就报错了!
三. 解决思路
既然我们现在明白了bug的产生原因,解决起来也就很容易了,只要把日期格式匹配上即可。现在springboot-data中写入的是long,只要让ES mapping中能够接收long格式即可。
1. 修改配置
修改后的代码配置如下所示:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "user_info",shards = 1,replicas = 1,createIndex = false)
public class UserInfo {
@Id
@Field(type = FieldType.Long)
private Long id;
@Field(type = FieldType.Keyword)
private String name;
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String remark;
// 这里不要写 format即可,默认的date类型就可以接收 long值
@Field(type = FieldType.Date)
private Date jobday;
}
2. mapping结构
mapping结构如下所示,这里的date就可以接收long类型的值了。
3. 单元测试
接下来我们重新进行单元测试,此时发现,代码可以正常执行通过了。
@Test
void save() {
final UserInfo userInfo = new UserInfo();
userInfo.setId(100l);
userInfo.setName("张三");
userInfo.setJobday(new Date());
userInfo.setRemark("张三是法外狂徒");
userRepository.save(userInfo);
}
现在你知道这个问题怎么解决了吗?以后再遇到类似的问题,不要慌,沉下心来仔细想一下问题出在哪里,然后一点点针对该问题进行分析解决。如果你实在解决不了,给壹哥私信,我来帮你搞定。关注Java架构栈,干货天天都不断哦!