從昨晚開始,到今天中午以前,一直在糾結時間存儲問題,昨晚是糾結時間取出來的問題。html
其實個人想法很簡單,我就想java.util.Date
存儲到 Elasticsearch ,而後從 Elasticsearch 中再取出來的時候,它是個Date
,不須要我任何轉換。java
可是發現好像不行。json
我開始在建立 Mapping 的時候,就是爲:app
//...省略部分代碼
.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();
插入後報錯:less
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
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
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 here
generator.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 case
generator.writeString(value.toString());
//throw new ElasticsearchIllegalArgumentException("type not supported for generic value conversion: " + type);
}
}
咱們看下這部分:XContentBuilder.defaultDatePrinter.print(((Date) value).getTime())
進去後。看到以下:this
/**
* 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
個小時。這個得注意。spa
總結了下。最終輸出都是String
類型。感受不友好。我本想的是,我無論存入是怎麼樣,我取出來得是Date
對象就能夠了。
關於時間類型說明:https://www.elastic.co/guide/en/elasticsearch/reference/current/date.html
JSON doesn’t have a date datatype, so dates in Elasticsearch can either be:
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:
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.
解決方案:
直接用毫秒值,缺點爲不直觀。
「yyyy-MM-dd HH:mm:ss」
而後存儲的時候,指定格式,而且 Mapping 也是指定相同的format
。解決方案:
JSON.toBean(json,POJO.class)
,就解決了,這裏利用的是相同 JSON 包轉成 JSON ,而後又toBean
回來,是沒問題的,而後 Elasticsearch 也支持存儲 JSON 。好了上面觀點純屬我的觀點。可能存在錯誤和參雜我的色彩。請勿做爲直接參考。錯誤的地方,請在下面留言。