当前位置:网站首页>ES 中时间日期类型 “yyyy-MM-dd HHmmss” 的完全避坑指南
ES 中时间日期类型 “yyyy-MM-dd HHmmss” 的完全避坑指南
2022-07-31 00:13:00 【drhrht】
文章目录
1、ES中的日期类型有何不同
时间和日期类型是我们作为开发每天都会遇到的一种常见数据类型。和Java
中有所不同,Elasticsearch
在索引创建之前并不是必须要创建索引的mapping。关系型数据库的思维就是在中写入数据之前,并不强制创建表结构。我们不用事先声明字段名称,字段类型以及长度等属性就可以直接像一个不存在的表中直接写入数据。
Elasticsearch
把这种特性称之为dynamic mapping
,也就是自动映射。Elasticsearch
会根据你写入的字段的内容动态去判定字段的数据类型,不过这种自动映射的机制存在一些缺陷,比如在Elasticsearch
中没有隐式类型转换,所以在自动映射的时候就会把字段映射为较宽的数据类型。比如你写入一个数字50,系统就会自动给你映射成long
类型,而不是int
。一般企业中用于生产的环境都是使用手工映射,能保证按需创建以节省资源和达到更高的性能。
但是在Elasticsearch
中,时间类型是一个非常容易踩坑的数据类型,通过一个例子向大家展示这个时间类型到底有多“坑”!
2、案例
2.1 案例介绍
假如我们有如下索引tax
,保存了一些公司的纳税或资产信息,单位为“万元”。当然这里面的数据是随意填写的。多少为数据统计的时间,当前这个例子里。索引达的含义并不重要。关键点在于字段的内容格式。我们看到date字段其中包含了多种日期的格式:“yyyy-MM-dd”,“yyyy-MM-dd”还有时间戳。如果按照dynamic mapping,采取自动映射器来映射索引。我们自然而然的都会感觉字段应该是一个date类型。
POST tax/_bulk
{"index":{}}
{"date": "2021-01-25 10:01:12", "company": "中国烟草", "ratal": 5700000}
{"index":{}}
{"date": "2021-01-25 10:01:13", "company": "华为", "ratal": 4034113.182}
{"index":{}}
{"date": "2021-01-26 10:02:11", "company": "苹果", "ratal": 7784.7252}
{"index":{}}
{"date": "2021-01-26 10:02:15", "company": "小米", "ratal": 185000}
{"index":{}}
{"date": "2021-01-26 10:01:23", "company": "阿里", "ratal": 1072526}
{"index":{}}
{"date": "2021-01-27 10:01:54", "company": "腾讯", "ratal": 6500}
{"index":{}}
{"date": "2021-01-28 10:01:32", "company": "蚂蚁金服", "ratal": 5000}
{"index":{}}
{"date": "2021-01-29 10:01:21", "company": "字节跳动", "ratal": 10000}
{"index":{}}
{"date": "2021-01-30 10:02:07", "company": "中国石油", "ratal": 18302097}
{"index":{}}
{"date": "1648100904", "company": "中国石化", "ratal": 32654722}
{"index":{}}
{"date": "2021-11-1 12:20:00", "company": "国家电网", "ratal": 82950000}
然而我们以上代码查看tax索引的mapping,会惊奇的发现date居然是一个text类型。这是为什么呢?
"properties" : {
"date" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
2.2 原理揭秘
原因就在于对时间类型的格式的要求是绝对严格的。要求必须是一个标准的UTC时间类型。上述字段的数据格式如果想要使用,就必须使用yyyy-MM-ddTHH:mm:ssZ
格式(其中T个间隔符,Z代表 0 时区),以下均为错误的时间格式(均无法被自动映射器识别为日期时间类型):
- yyyy-MM-dd HH:mm:ss
- yyyy-MM-dd
- 时间戳
注意:需要注意的是时间说是必须的时间格式,但是需要通过手工映射方式在索引创建之前指定为日期类型,使用自动映射器无法映射为日期类型。
3、路为何这么不平
我们现在已经知道要求其类型必须为UTC的时间格式,那么我们把下面索引通过自动映射,date字段会被映射为什么类型呢?
PUT test_index/_doc/1
{
"time":"2022-4-30T20:00:00Z"
}
执行代码,我们来看一下结果:
历史总是惊人的相似,映射结果居然依然是文本类型。这就是又一个我们很容易踩的坑,日期字段并非严格符合要求格式。
注意观察下面两者区别:
- 2022-4-30T20:00:00Z 错误
- 2022-04-30T20:00:00Z 正确
应该不用再用我解释什么了吧 O(_)O哈哈~
4、又一个坑
你以为这样就结束了吗?
如果我们换一个思路,使用手工映射提前指定日期类型,那会又是一个什么结果呢?
PUT tax
{
"mappings": {
"properties": {
"date": {
"type": "date"
}
}
}
}
POST tax/_bulk
{"index":{}}
{"date": "2021-01-30 10:02:07", "company": "中国石油", "ratal": 18302097}
{"index":{}}
{"date": "1648100904", "company": "中国石化", "ratal": 32654722}
{"index":{}}
{"date": "2021-11-1T12:20:00Z", "company": "国家电网", "ratal": 82950000}
{"index":{}}
{"date": "2021-01-30T10:02:07Z", "company": "中国石油", "ratal": 18302097}
{"index":{}}
{"date": "2021-01-25", "company": "中国烟草", "ratal": 5700000}
执行以上代码,以下为完整的执行结果:
{
"took" : 17,
"errors" : true,
"items" : [
{
"index" : {
"_index" : "tax",
"_type" : "_doc",
"_id" : "f4uyun8B1ovRQq6Sn9Qg",
"status" : 400,
"error" : {
"type" : "mapper_parsing_exception",
"reason" : "failed to parse field [date] of type [date] in document with id 'f4uyun8B1ovRQq6Sn9Qg'. Preview of field's value: '2021-01-30 10:02:07'",
"caused_by" : {
"type" : "illegal_argument_exception",
"reason" : "failed to parse date field [2021-01-30 10:02:07] with format [strict_date_optional_time||epoch_millis]",
"caused_by" : {
"type" : "date_time_parse_exception",
"reason" : "date_time_parse_exception: Failed to parse with all enclosed parsers"
}
}
}
}
},
{
"index" : {
"_index" : "tax",
"_type" : "_doc",
"_id" : "gIuyun8B1ovRQq6Sn9Qg",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 2,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1,
"status" : 201
}
},
{
"index" : {
"_index" : "tax",
"_type" : "_doc",
"_id" : "gYuyun8B1ovRQq6Sn9Qg",
"status" : 400,
"error" : {
"type" : "mapper_parsing_exception",
"reason" : "failed to parse field [date] of type [date] in document with id 'gYuyun8B1ovRQq6Sn9Qg'. Preview of field's value: '2021-11-1T12:20:00Z'",
"caused_by" : {
"type" : "illegal_argument_exception",
"reason" : "failed to parse date field [2021-11-1T12:20:00Z] with format [strict_date_optional_time||epoch_millis]",
"caused_by" : {
"type" : "date_time_parse_exception",
"reason" : "date_time_parse_exception: Failed to parse with all enclosed parsers"
}
}
}
}
},
{
"index" : {
"_index" : "tax",
"_type" : "_doc",
"_id" : "gouyun8B1ovRQq6Sn9Qg",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 2,
"failed" : 0
},
"_seq_no" : 4,
"_primary_term" : 1,
"status" : 201
}
},
{
"index" : {
"_index" : "tax",
"_type" : "_doc",
"_id" : "g4uyun8B1ovRQq6Sn9Qg",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 2,
"failed" : 0
},
"_seq_no" : 5,
"_primary_term" : 1,
"status" : 201
}
}
]
}
分析:
- 第一个(写入失败):2021-01-30 10:02:07
- 第二个(写入成功):1648100904
- 第三个(写入失败):2021-11-1T12:20:00Z
- 第四个(写入成功):2021-01-30T10:02:07Z
- 第五个(写入成功):2021-01-25
5、总结
- 对于
yyyy-MM-dd HH:mm:ss
或2021-11-1T12:20:00Z
,ES 的自动映射器完全无法识别,即便是事先声明日期类型,数据强行写入也会失败。 - 对于
时间戳
和yyyy-MM-dd
这样的时间格式,ES 自动映射器无法识别,但是如果事先说明了日期类型是可以正常写入的。 - 对于标准的日期时间类型是可以正常自动识别为日期类型,并且也可以通过手工映射来实现声明字段类型。
6、ES 的时间类型为什么这么难用,有没有什么办法可以解决?
有,当然有,必须有,关注我就对了 ??
其实解决办法非常简单。只需要在字段属性中添加一个参数:
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
,这样就可以避免因为数据格式不统一而导致数据无法写入的窘境。代码如下:
PUT test_index
{
"mappings": {
"properties": {
"time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
7、更优的生产解决方案
那么问题来了,如果我们生产环境中的数据已经是text
类型,无法按照时间进行检索,只能Reindex吗?
当然不是!那样也太Low了!更优解决方案,推荐阅读:不必Reindex,利用runtime_fields优雅地解决字段类型错误问题
这么干货,还不赶快分享与点赞!
加入,更多原创干货与你分享!
先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦
边栏推荐
猜你喜欢
【唐宇迪 深度学习-3D点云实战系列】学习笔记
How to solve types joiplay simulator does not support this game
HCIP第十六天笔记
封装、获取系统用户信息、角色及权限控制
会议OA项目待开会议、所有会议功能
测试人面试 常被问到的计算机网络题,高薪回答模板来了
46.<list链表的举列>
How to import game archives in joiplay emulator
Chevrolet Trailblazer, the first choice for safety and warmth for your family travel
Shell programming conditional statement test command Integer value, string comparison Logical test File test
随机推荐
Axure轮播图
状态机动态规划之股票问题总结
Ukraine's foreign ministry: wu was restored to complete the export of food security
xss靶机训练【实现弹窗即成功】
flex布局父项常见属性flex-wrap
IOT跨平台组件设计方案
会议OA项目待开会议、所有会议功能
The first level must project independently
软件测试三阶段,你在哪一步?
How to import game archives in joiplay emulator
实验7(MPLS实验)
天空云变化案例
DNS解析过程【访问网站】
如何在WordPress网站上添加导航菜单
实验8(vlan实验)
Steven Giesel 最近发布了一个由5部分内容组成的系列,记录了他首次使用 Uno Platform 构建应用程序的经验。
HCIP第十五天笔记
从编译的角度来学作用域!
写了多年业务代码,我发现了这11个门道,只有内行才知道
[Meng Xin problem solving] Delete the Nth node from the bottom of the linked list