您的位置:首页 > 其它

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

2017-02-17 16:35 441 查看
从昨晚开始,到今天中午之前,一直在纠结时间存储问题,昨晚是纠结时间取出来的问题。

其实我的想法很简单,我就想
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 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())
 进去后。看到如下:

/**

* 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  

好了上面观点纯属个人观点。可能存在错误和参杂个人色彩。请勿作为直接参考。错误的地方,请在下面留言。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  elasticsearch
相关文章推荐