Elasticsearch Date類型,時間存儲相關說明

從昨晚開始,到今天中午以前,一直在糾結時間存儲問題,昨晚是糾結時間取出來的問題。html

其實個人想法很簡單,我就想java.util.Date  存儲到 Elasticsearch  ,而後從 Elasticsearch  中再取出來的時候,它是個Date ,不須要我任何轉換。java

可是發現好像不行。json

我開始在建立 Mapping  的時候,就是爲:app

 
  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(); 插入後報錯:less

 
  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"];]

 

根據錯誤提示,我先把時間格式化,而後插入:elasticsearch

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

而後插入OK。後來我看了源碼,才恍然大悟。新版本(我不知道從什麼版本開始,我之前最開始用的是0.9)值是根據value 的類型來判斷。我貼一下。ide

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

 
  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()) 進去後。看到以下:this

 
  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 instant millis since 1970-01-01T00:00:00Z
  8. * @return 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 個小時。這個得注意。spa

總結了下。最終輸出都是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?

    解決方案:

好了上面觀點純屬我的觀點。可能存在錯誤和參雜我的色彩。請勿做爲直接參考。錯誤的地方,請在下面留言。

相關文章
相關標籤/搜索