Elasticsearch Date类型,时间存储相关说明

Stella981
• 阅读 603

从昨晚开始,到今天中午之前,一直在纠结时间存储问题,昨晚是纠结时间取出来的问题。

其实我的想法很简单,我就想java.util.Date  存储到 Elasticsearch ,然后从 Elasticsearch 中再取出来的时候,它是个Date ,不需要我任何转换。

但是发现好像不行。

我开始在创建 Mapping 的时候,就是为:

  1. //...省略部分代码
  2. .startObject("create_date").field("type","date").field("format","yyyy-MM-dd HH:mm:ss").endObject()
  3. //...省略部分代码

指定了TypeDate ,并且formatyyyy-MM-dd HH:mm:ss ,然后new Date(); 插入后报错:

  1. message [MapperParsingException[failed to parse [create_date]]; nested: IllegalArgumentException[Invalid format: "2016-07-04T03:03:12.616Z" is malformed at "T03:03:12.616Z"];]

根据错误提示,我先把时间格式化,然后插入:

  1. result.put("create_date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(create_date));

然后插入OK。后来我看了源码,才恍然大悟。新版本(我不知道从什么版本开始,我以前最开始用的是0.9)值是根据value 的类型来判断。我贴一下。

org.elasticsearch.common.xcontent.XContentBuilder  中 1248 行。

  1. private void writeValue(Object value) throws IOException {
  2. if (value == null) {
  3. generator.writeNull();
  4. return;
  5. }
  6. Class<?> type = value.getClass();
  7. if (type == String.class) {
  8. generator.writeString((String) value);
  9. } else if (type == Integer.class) {
  10. generator.writeNumber(((Integer) value).intValue());
  11. } else if (type == Long.class) {
  12. generator.writeNumber(((Long) value).longValue());
  13. } else if (type == Float.class) {
  14. generator.writeNumber(((Float) value).floatValue());
  15. } else if (type == Double.class) {
  16. generator.writeNumber(((Double) value).doubleValue());
  17. } else if (type == Byte.class) {
  18. generator.writeNumber(((Byte)value).byteValue());
  19. } else if (type == Short.class) {
  20. generator.writeNumber(((Short) value).shortValue());
  21. } else if (type == Boolean.class) {
  22. generator.writeBoolean(((Boolean) value).booleanValue());
  23. } else if (type == GeoPoint.class) {
  24. generator.writeStartObject();
  25. generator.writeNumberField("lat", ((GeoPoint) value).lat());
  26. generator.writeNumberField("lon", ((GeoPoint) value).lon());
  27. generator.writeEndObject();
  28. } else if (value instanceof Map) {
  29. writeMap((Map) value);
  30. } else if (value instanceof Path) {
  31. //Path implements Iterable<Path> and causes endless recursion and a StackOverFlow if treated as an Iterable here
  32. generator.writeString(value.toString());
  33. } else if (value instanceof Iterable) {
  34. generator.writeStartArray();
  35. for (Object v : (Iterable<?>) value) {
  36. writeValue(v);
  37. }
  38. generator.writeEndArray();
  39. } else if (value instanceof Object[]) {
  40. generator.writeStartArray();
  41. for (Object v : (Object[]) value) {
  42. writeValue(v);
  43. }
  44. generator.writeEndArray();
  45. } else if (type == byte[].class) {
  46. generator.writeBinary((byte[]) value);
  47. /* 注意这里:如果是Date类型,就是以字符串输出。
  48. 如果你跟进去看。代码在下个片段。
  49. */
  50. } else if (value instanceof Date) {
  51. generator.writeString(XContentBuilder.defaultDatePrinter.print(((Date) value).getTime()));
  52. } else if (value instanceof Calendar) {
  53. generator.writeString(XContentBuilder.defaultDatePrinter.print((((Calendar) value)).getTimeInMillis()));
  54. } else if (value instanceof ReadableInstant) {
  55. generator.writeString(XContentBuilder.defaultDatePrinter.print((((ReadableInstant) value)).getMillis()));
  56. } else if (value instanceof BytesReference) {
  57. BytesReference bytes = (BytesReference) value;
  58. if (!bytes.hasArray()) {
  59. bytes = bytes.toBytesArray();
  60. }
  61. generator.writeBinary(bytes.array(), bytes.arrayOffset(), bytes.length());
  62. } else if (value instanceof BytesRef) {
  63. BytesRef bytes = (BytesRef) value;
  64. generator.writeBinary(bytes.bytes, bytes.offset, bytes.length);
  65. } else if (value instanceof Text) {
  66. Text text = (Text) value;
  67. if (text.hasBytes() && text.bytes().hasArray()) {
  68. generator.writeUTF8String(text.bytes().array(), text.bytes().arrayOffset(), text.bytes().length());
  69. } else if (text.hasString()) {
  70. generator.writeString(text.string());
  71. } else {
  72. BytesArray bytesArray = text.bytes().toBytesArray();
  73. generator.writeUTF8String(bytesArray.array(), bytesArray.arrayOffset(), bytesArray.length());
  74. }
  75. } else if (value instanceof ToXContent) {
  76. ((ToXContent) value).toXContent(this, ToXContent.EMPTY_PARAMS);
  77. } else if (value instanceof double[]) {
  78. generator.writeStartArray();
  79. for (double v : (double[]) value) {
  80. generator.writeNumber(v);
  81. }
  82. generator.writeEndArray();
  83. } else if (value instanceof long[]) {
  84. generator.writeStartArray();
  85. for (long v : (long[]) value) {
  86. generator.writeNumber(v);
  87. }
  88. generator.writeEndArray();
  89. } else if (value instanceof int[]) {
  90. generator.writeStartArray();
  91. for (int v : (int[]) value) {
  92. generator.writeNumber(v);
  93. }
  94. generator.writeEndArray();
  95. } else if (value instanceof float[]) {
  96. generator.writeStartArray();
  97. for (float v : (float[]) value) {
  98. generator.writeNumber(v);
  99. }
  100. generator.writeEndArray();
  101. } else if (value instanceof short[]) {
  102. generator.writeStartArray();
  103. for (short v : (short[]) value) {
  104. generator.writeNumber(v);
  105. }
  106. generator.writeEndArray();
  107. } else {
  108. // if this is a "value" object, like enum, DistanceUnit, ..., just toString it
  109. // yea, it can be misleading when toString a Java class, but really, jackson should be used in that case
  110. generator.writeString(value.toString());
  111. //throw new ElasticsearchIllegalArgumentException("type not supported for generic value conversion: " + type);
  112. }
  113. }

我们看下这部分:XContentBuilder.defaultDatePrinter.print(((Date) value).getTime()) 进去后。看到如下:

  1. /**
  2. * Prints a millisecond instant to a String.
  3. * <p>
  4. * This method will use the override zone and the override chronology if
  5. * they are set. Otherwise it will use the ISO chronology and default zone.
  6. *
  7. * [@param](http://my.oschina.net/u/2303379) instant millis since 1970-01-01T00:00:00Z
  8. * [@return](http://my.oschina.net/u/556800) the printed result
  9. */
  10. public String print(long instant) {
  11. StringBuilder buf = new StringBuilder(requirePrinter().estimatePrintedLength());
  12. try {
  13. printTo((Appendable) buf, instant);
  14. } catch (IOException ex) {
  15. // StringBuilder does not throw IOException
  16. }
  17. return buf.toString();
  18. }

看到这里就明白了吧。他最终的输出方式都是以字符串输出,只是默认的格式是:1970-01-01T00:00:00Z ,也就是默认的 UTC 格式。我的时间转换结果成:2016-07-04T03:03:12.616Z 这里并且有时区的概念,东八区,这里输出的时间少了8 个小时。这个得注意。

总结了下。最终输出都是String 类型。感觉不友好。我本想的是,我不管存入是怎么样,我取出来得是Date 对象就可以了。

官网时间(Date)格式说明

关于时间类型说明:https://www.elastic.co/guide/en/elasticsearch/reference/current/date.html

关于时间类型格式化:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#strict-date-time

JSON doesn’t have a date datatype, so dates in Elasticsearch can either be:

  • strings containing formatted dates, e.g. "2015-01-01" or "2015/01/01 12:10:30".
  • a long number representing milliseconds-since-the-epoch.
  • an integer representing seconds-since-the-epoch.

Internally, dates are converted to UTC (if the time-zone is specified) and stored as a long number representing milliseconds-since-the-epoch.

Date formats can be customised, but if no format is specified then it uses the default:

  1. "strict_date_optional_time||epoch_millis"

This means that it will accept dates with optional timestamps, which conform to the formats supported by strict_date_optional_time or milliseconds-since-the-epoch.

解决方法及问题:

1.时间输出格式,如果是默认UTC格式,时间不是我们常用的格式,而且时区问题,少了8个小时。

    解决方案:

  • 直接用毫秒值,缺点为不直观。

  • 直接设置format为你想要的格式,比如“yyyy-MM-dd HH:mm:ss” 然后存储的时候,指定格式,并且 Mapping 也是指定相同的format

2.存储Date,和取出来也是Dete?

    解决方案:

  • 存储的时候利用各种JSON对象,比如 json-libfastjsonJacksongson 等等。存储的时候就可以用JSON Format一下再存储,然后取出来后,在用JSON.toBean(json,POJO.class) ,就解决了,这里利用的是相同 JSON 包转成 JSON ,然后又toBean 回来,是没问题的,然后 Elasticsearch 也支持存储 JSON

好了上面观点纯属个人观点。可能存在错误和参杂个人色彩。请勿作为直接参考。错误的地方,请在下面留言。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这