Elasticsearch date数据类型

发布时间 2023-07-17 14:12:38作者: 章怀柔

时间和日期类型是我们作为开发每天都会遇到的一种常见数据类型。和Java中有所不同,Elasticsearch 在索引创建之前并不是必须要创建索引的mapping。关系型数据库的思维就是在于写入数据之前,并不强制创建表结构。我们不用事先声明字段名称,字段类型以及长度等属性就可以直接向一个不存在的表中直接写入数据。

Elasticsearch把这种特性称之为dynamic mapping,也就是自动映射。Elasticsearch会根据你写入的字段的内容动态去判定字段的数据类型,不过这种自动映射的机制存在一些缺陷,比如在Elasticsearch中没有隐式类型转换,所以在自动映射的时候就会把字段映射为较宽的数据类型。比如你写入一个数字50,系统就会自动给你映射成long类型,而不是int 。一般企业中用于生产的环境都是使用手工映射,能保证按需创建以节省资源和达到更高的性能。

但是在Elasticsearch中,时间类型是一个非常容易踩坑的数据类型,通过一个例子向大家展示这个时间类型到底有多“坑”!

案例

假如我们有如下索引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
      }
    }
  }
}

  

 

原理揭秘

原因就在于对时间类型的格式的要求是绝对严格的。要求必须是一个标准的UTC时间类型。上述字段的数据格式如果想要使用,就必须使用yyyy-MM-ddTHH:mm:ssZ格式(其中T个间隔符,Z代表 0 时区),以下均为错误的时间格式(均无法被自动映射器识别为日期时间类型):

•yyyy-MM-dd HH:mm:ss•yyyy-MM-dd•时间戳

路为何这么不平

我们现在已经知道要求其类型必须为UTC的时间格式,那么我们把下面索引通过自动映射,time字段会被映射成什么类型呢?

PUT test_index/_doc/1
{
  "time":"2022-4-30T20:00:00Z"
}

  

执行代码,我们来看一下结果:

 

历史总是惊人的相似,映射结果居然依然是文本类型。

这就是又一个我们很容易踩的坑,日期字段并非严格符合要求格式。

 

注意观察下面两者区别:

•2022-4-30T20:00:00Z 错误•2022-04-30T20:00:00Z 正确

应该不用再用我解释什么了吧 O(^_^)O哈哈~

对,又双叒叕一个坑

你以为这样就结束了吗?

如果我们换一个思路,使用手工映射提前指定日期类型,那会又是一个什么结果呢?

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}

  


执行以上代码,以下为完整的执行结果:

分析:

•第一个(写入失败):2021-01-30 10:02:07•第二个(写入成功):1648100904•第三个(写入失败):2021-11-1T12:20:00Z•第四个(写入成功):2021-01-30T10:02:07Z•第五个(写入成功):2021-01-25

总结

•对于yyyy-MM-dd HH:mm:ss2021-11-1T12:20:00Z,ES 的自动映射器完全无法识别,即便是事先声明日期类型,数据强行写入也会失败。•对于时间戳yyyy-MM-dd这样的时间格式,ES 自动映射器无法识别,但是如果事先说明了日期类型是可以正常写入的。•对于标准的日期时间类型是可以正常自动识别为日期类型,并且也可以通过手工映射来实现声明字段类型。

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"
      }
    }
  }
}

  

转载自 Elastic之家公众号