從昨晚開始,到今天中午之前,一直在糾結時間存儲問題,昨晚是糾結時間取出來的問題。
其實我的想法很簡單,我就想java.util.Date 存儲到 Elasticsearch ,然后從 Elasticsearch 中再取出來的時候,它是個Date ,不需要我任何轉換。
但是發現好像不行。
我開始在創建 Mapping 的時候,就是為:
//...省略部分代碼.startObject("create_date").field("type","date").field("format","yyyy-MM-dd HH:mm:ss").endObject()//...省略部分代碼指定了
Type為Date,并且format為yyyy-MM-dd HH:mm:ss,然后new Date();插入后報錯:message [MapperParsingException[failed to parse [create_date]]; nested: IllegalArgumentException[Invalid format: "2016-07-04T03:03:12.616Z" is malformed at "T03:03:12.616Z"];]根據錯誤提示,我先把時間格式化,然后插入:
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行。PRivate void writeValue(Object value) throws IOException {if (value == null) {generator.writeNull();return;}Class<?> type = value.getClass();if (type == String.class) {generator.writeString((String) value);} else if (type == Integer.class) {generator.writeNumber(((Integer) value).intValue());} else if (type == Long.class) {generator.writeNumber(((Long) value).longValue());} else if (type == Float.class) {generator.writeNumber(((Float) value).floatValue());} else if (type == Double.class) {generator.writeNumber(((Double) value).doubleValue());} else if (type == Byte.class) {generator.writeNumber(((Byte)value).byteValue());} else if (type == Short.class) {generator.writeNumber(((Short) value).shortValue());} else if (type == Boolean.class) {generator.writeBoolean(((Boolean) value).booleanValue());} else if (type == GeoPoint.class) {generator.writeStartObject();generator.writeNumberField("lat", ((GeoPoint) value).lat());generator.writeNumberField("lon", ((GeoPoint) value).lon());generator.writeEndObject();} else if (value instanceof Map) {writeMap((Map) value);} else if (value instanceof Path) {//Path implements Iterable<Path> and causes endless recursion and a StackOverFlow if treated as an Iterable heregenerator.writeString(value.toString());} else if (value instanceof Iterable) {generator.writeStartArray();for (Object v : (Iterable<?>) value) {writeValue(v);}generator.writeEndArray();} else if (value instanceof Object[]) {generator.writeStartArray();for (Object v : (Object[]) value) {writeValue(v);}generator.writeEndArray();} else if (type == byte[].class) {generator.writeBinary((byte[]) value);/* 注意這里:如果是Date類型,就是以字符串輸出。如果你跟進去看。代碼在下個片段。*/} else if (value instanceof Date) {generator.writeString(XContentBuilder.defaultDatePrinter.print(((Date) value).getTime()));} else if (value instanceof Calendar) {generator.writeString(XContentBuilder.defaultDatePrinter.print((((Calendar) value)).getTimeInMillis()));} else if (value instanceof ReadableInstant) {generator.writeString(XContentBuilder.defaultDatePrinter.print((((ReadableInstant) value)).getMillis()));} else if (value instanceof BytesReference) {BytesReference bytes = (BytesReference) value;if (!bytes.hasArray()) {bytes = bytes.toBytesArray();}generator.writeBinary(bytes.array(), bytes.arrayOffset(), bytes.length());} else if (value instanceof BytesRef) {BytesRef bytes = (BytesRef) value;generator.writeBinary(bytes.bytes, bytes.offset, bytes.length);} else if (value instanceof Text) {Text text = (Text) value;if (text.hasBytes() && text.bytes().hasArray()) {generator.writeUTF8String(text.bytes().array(), text.bytes().arrayOffset(), text.bytes().length());} else if (text.hasString()) {generator.writeString(text.string());} else {BytesArray bytesArray = text.bytes().toBytesArray();generator.writeUTF8String(bytesArray.array(), bytesArray.arrayOffset(), bytesArray.length());}} else if (value instanceof ToXContent) {((ToXContent) value).toXContent(this, ToXContent.EMPTY_PARAMS);} else if (value instanceof double[]) {generator.writeStartArray();for (double v : (double[]) value) {generator.writeNumber(v);}generator.writeEndArray();} else if (value instanceof long[]) {generator.writeStartArray();for (long v : (long[]) value) {generator.writeNumber(v);}generator.writeEndArray();} else if (value instanceof int[]) {generator.writeStartArray();for (int v : (int[]) value) {generator.writeNumber(v);}generator.writeEndArray();} else if (value instanceof float[]) {generator.writeStartArray();for (float v : (float[]) value) {generator.writeNumber(v);}generator.writeEndArray();} else if (value instanceof short[]) {generator.writeStartArray();for (short v : (short[]) value) {generator.writeNumber(v);}generator.writeEndArray();} else {// if this is a "value" object, like enum, DistanceUnit, ..., just toString it// yea, it can be misleading when toString a Java class, but really, jackson should be used in that casegenerator.writeString(value.toString());//throw new ElasticsearchIllegalArgumentException("type not supported for generic value conversion: " + type);}}我們看下這部分:
XContentBuilder.defaultDatePrinter.print(((Date) value).getTime())進去后。看到如下:/*** Prints a millisecond instant to a String.* <p>* This method will use the override zone and the override chronology if* they are set. Otherwise it will use the ISO chronology and default zone.** @param instant millis since 1970-01-01T00:00:00Z* @return the printed result*/public String print(long instant) {StringBuilder buf = new StringBuilder(requirePrinter().estimatePrintedLength());try {printTo((Appendable) buf, instant);} catch (IOException ex) {// StringBuilder does not throw IOException}return buf.toString();}看到這里就明白了吧。他最終的輸出方式都是以字符串輸出,只是默認的格式是:
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:
"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-lib , fastjson , Jackson , gson 等等。存儲的時候就可以用JSON Format一下再存儲,然后取出來后,在用JSON.toBean(json,POJO.class),就解決了,這里利用的是相同 JSON 包轉成 JSON ,然后又toBean回來,是沒問題的,然后 Elasticsearch 也支持存儲 JSON 。好了上面觀點純屬個人觀點。可能存在錯誤和參雜個人色彩。請勿作為直接參考。錯誤的地方,請在下面留言。
新聞熱點
疑難解答