ElasticSearch往索引库中插入文档时报错:failed to parse field [updateTime] of type [date] in document

1. 问题复现

1.1 索引库的结构

索引库的结构如下(其中 updateTime 字段为 date 类型)

在这里插入图片描述

{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "price": {
        "type": "integer"
      },
      "image": {
        "type": "keyword",
        "index": false
      },
      "category": {
        "type": "keyword"
      },
      "brand": {
        "type": "keyword"
      },
      "sold": {
        "type": "integer"
      },
      "commentCount": {
        "type": "integer",
        "index": false
      },
      "isAD": {
        "type": "boolean"
      },
      "updateTime": {
        "type": "date"
      }
    }
  }
}

1.2 问题呈现

在 Java 代码中利用 BulkRequest 向 ElasticSearch 发送批量新增文档请求,发现没有一条数据添加成功,在控制台打印 bulk 方法返回的 BulkResponse 对象和遍历 BulkResponse 对象的 getItems() 方法返回的数组,都没有详细的报错信息

我百思不得其解,于是我单独拿出一条已经转换为 JSON 格式的数据,在 kibana 提供的 Dev Tools 控制台中往索引库新增一条文档

在这里插入图片描述

PUT /shopping_mall/_doc/317578
{
  "brand": "RIMOWA",
  "category": "拉杆箱",
  "commentCount": 0,
  "image": "https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp",
  "isAD": false,
  "name": "RIMOWA 21寸托运箱拉杆箱 SALSA AIR系列果绿色 820.70.36.4",
  "price": 28900,
  "sold": 0,
  "stock": 10000,
  "updateTime": "2023-05-06 11:06:17"
}

发送新增文档的请求后,在 Dev Tools 控制台看到了以下错误信息

在这里插入图片描述

{
“error” : {
“root_cause” : [
{
“type” : “mapper_parsing_exception”,
“reason” : “failed to parse field [updateTime] of type [date] in document with id ‘317578’. Preview of field’s value: ‘2023-05-06 11:06:17’”
}
],
“type” : “mapper_parsing_exception”,
“reason” : “failed to parse field [updateTime] of type [date] in document with id ‘317578’. Preview of field’s value: ‘2023-05-06 11:06:17’”,
“caused_by” : {
“type” : “illegal_argument_exception”,
“reason” : “failed to parse date field [2023-05-06 11:06:17] with format [strict_date_optional_time||epoch_millis]”,
“caused_by” : {
“type” : “date_time_parse_exception”,
“reason” : “Failed to parse with all enclosed parsers”
}
}
},
“status” : 400
}

1.3 问题剖析

Dev Tools 控制台给出的错误信息表明,Elasticsearch 在尝试将字符串 '2023-05-06 11:06:17' 解析为日期类型时,遇到了问题


ElasticSearch 默认支持两种日期格式:

  1. strict_date_optional_time
  2. epoch_millis:自 1970 年 1 月 1 日 00:00:00 UTC 以来的毫秒数

以下是一些有效的 strict_date_optional_time 格式的示例:

  • 2023-05-06(仅日期)
  • 2023-05-06T11:06:17(日期和时间,无时区)
  • 2023-05-06T11:06:17Z(日期和时间,UTC 时区)
  • 2023-05-06T11:06:17+01:00(日期和时间,带时区偏移量)

以下是一些无效的 strict_date_optional_time 格式的示例:

  • 06-05-2023(日期部分的顺序不正确)
  • 2023/05/06(日期部分使用了错误的分隔符)
  • 2023-05-06 11:06:17(时间部分没有使用 T 分隔符)

ElasticSearch 在解析提供的日期字符串时失败了,因为提供的日期字符串 '2023-05-06 11:06:17' 并不匹配 ElasticSearch 默认支持的两种日期格式

2. 解决方案

2.1 在创建索引库时指定支持的日期格式(推荐)

我们在创建索引库时,可以指定支持的日期格式(通过 format 属性指定,format 属性是一个数组,可以接收多个日期格式)

在这里插入图片描述

{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "price": {
        "type": "integer"
      },
      "image": {
        "type": "keyword",
        "index": false
      },
      "category": {
        "type": "keyword"
      },
      "brand": {
        "type": "keyword"
      },
      "sold": {
        "type": "integer"
      },
      "commentCount": {
        "type": "integer",
        "index": false
      },
      "isAD": {
        "type": "boolean"
      },
      "updateTime": {
        "type": "date",
        "format": ["yyyy-MM-dd HH:mm:ss"]
      }
    }
  }
}

2.2 使用自定义的序列化器(使用 fastjson2,推荐)

实体类中 updateTime 字段使用的是 LocalDatetime 类型,数据库中 updateTime 字段是 datetime 类型(格式为 yyyy-MM-dd HH:mm:ss ),数据库中的 updateTime 字段转换为 LocalDatetime 类型时,会丢失纳秒级别的精度

丢失了纳秒级别的精度后,fastjson2 在将 LocalDatetime 类型的字段序列化为 JSON 时,日期格式为 yyyy-MM-dd HH:mm:ss,我们可以自定义序列化规则

2.2.1 自定义序列化器

自定义一个类,实现 ObjectWriter 接口,重写 write 方法

import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.writer.ObjectWriter;

import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeWriter implements ObjectWriter<LocalDateTime> {

    // 私有构造函数,防止外部直接创建实例
    private LocalDateTimeWriter() {

    }

    private static final LocalDateTimeWriter INSTANCE = new LocalDateTimeWriter();

    // 提供一个公共的静态方法,用于获取单例
    public static LocalDateTimeWriter getInstance() {
        return INSTANCE;
    }
    
    @Override
    public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type type, long features) {
        // 检查对象是否为 LocalDateTime 类型
        if (object instanceof LocalDateTime) {
            // 使用指定的格式将 LocalDateTime 对象转换为字符串
            jsonWriter.writeString(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").format((LocalDateTime) object));
        } else {
            // 对于其他类型的对象,直接使用 JSON 序列化器进行处理
            jsonWriter.writeAny(object);
        }
    }

}

2.2.2 注册序列化器

fastjson2 提供了 JSON.register(Type type, ObjectWriter<?> objectWriter) 方法,该方法可以注册指定类型的序列化器,在使用时默认全局生效

JSON.register(LocalDateTime.class, LocalDateTimeWriter.getInstance());

2.3 在实体类的日期字段上添加 @JSONField 注解(使用 fastjson2,不推荐)

不推荐使用该方式,因为该注解会同时影响序列化和反序列化时的日期格式

在实体类的日期字段上添加 @JSONField 注解后,会按指定的日期格式进行序列化和反序列化

@JSONField(format = "yyyy-MM-dd'T'HH:mm:ss")

ter.getInstance());




## 2.3 在实体类的日期字段上添加 @JSONField 注解(使用 fastjson2,不推荐)

>   ***不推荐使用该方式,因为该注解会同时影响序列化和反序列化时的日期格式***

在实体类的日期字段上添加 @JSONField 注解后,***会按指定的日期格式进行序列化和反序列化***

```java
@JSONField(format = "yyyy-MM-dd'T'HH:mm:ss")

在这里插入图片描述

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聂 可 以

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值