• ES 中时间日期类型 “yyyy-MM-dd HHmmss” 的完全避坑指南


    文章目录

    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}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    然而我们以上代码查看tax索引的mapping,会惊奇的发现date居然是一个text类型。这是为什么呢?

    "properties" : {
      "date" : {
        "type" : "text",
        "fields" : {
          "keyword" : {
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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"
    }
    
    • 1
    • 2
    • 3
    • 4

    执行代码,我们来看一下结果:
    在这里插入图片描述
    历史总是惊人的相似,映射结果居然依然是文本类型。这就是又一个我们很容易踩的坑,日期字段并非严格符合要求格式。

    注意观察下面两者区别:

    • 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}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    {
      "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
          }
        }
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    分析:

    • 第一个(写入失败):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:ss2021-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"
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    7、更优的生产解决方案

    那么问题来了,如果我们生产环境中的数据已经是text类型,无法按照时间进行检索,只能Reindex吗?

    当然不是!那样也太Low了!更优解决方案,推荐阅读:不必Reindex,利用runtime_fields优雅地解决字段类型错误问题

    这么干货,还不赶快分享与点赞!

    加入,更多原创干货与你分享!

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    云服务器和物理服务器的区别在哪
    pandas使用str函数和contains函数删除dataframe中单个指定字符串数据列包含特定字符串列表中的其中任何一个字符串的数据行
    VectorDraw Developer Framework 10.1004 Crack
    安装 fcitx + 搜狗/谷歌输入法 之后导致 死机,重启后黑屏只有鼠标可以移动
    ES6-Symbol 笔记 | Symbol 值作为对象的属性名 | Object.getOwnPropertySymbols()
    【语法基础练习】1.变量、输入输出、表达式与顺序语句
    YOLO-NAS详细教程-实现图像分割
    python批量读取nc气象数据并转为tif
    rust-强化练习
    【文献阅读】移动边缘计算中基于CNN模型分割的计算适配和负载均衡研究_黄煜
  • 原文地址:https://blog.csdn.net/drhrht/article/details/126071168