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

soゝso 2016-08-22 18:53:14 15625

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

其实我的想法很简单,我就想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?

    解决方案:

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


版权所属:SO JSON在线解析

原文地址:https://www.sojson.com/blog/149.html

转载时必须以链接形式注明原始出处及本声明。

本文主题:

如果本文对你有帮助,那么请你赞助我,让我更有激情的写下去,帮助更多的人。

关于作者
目前就职于国内某电商平台公司打杂。。
相关文章
Elasticsearch Date类型时间存储相关说明
Elasticsearch 教程,Elasticsearch 日期查询详解,Elasticsearch Date 查询Java API
Elasticsearch教程(五) elasticsearch Mapping的创建
Elasticsearch教程(六) elasticsearch Client创建
Elasticsearch教程,Elasticsearch Java API创建Mapping,指定分词器
Elasticsearch教程,Elasticsearch安全篇,通过Nginx http basic 限制访问
Elasticsearch教程 ,Elasticsearch count 查询,Elasticsearch 查询是否存在
Elasticsearch教程,Elasticsearch配置文件 — elasticsearch.yml
Elasticsearch权威指南-中文.pdf,Elasticsearch 中文文档下载
Elasticsearch 分词,Elasticsearch Java API 分词 操作
最新文章
天气API,全国天气 JSON API接口,可以获取十五天的天气预报 5
Springboot + Freemarker 集成配置 35
DNS查询, DNS测速检查接口,DNS测速接口 17
Mysql 并发插入、存在不插入,存在更新操作 33
SOJSON 阿里云活动达到67人,最终获得5年的1核2GB的ECS服务器使用权益 25
Java 完美解析.plist & 生成plist ,Android 解析.plist 26
Java SHA1的几种实现方式,Java SHA1 签名 50
Mac下Charles-proxy 抓包工具,iPhone 抓包演示 16
Mac Fidder 抓包替代方案 Charles-proxy 抓包工具下载 + 安装 + 破解 charles.jar 下载 171
阿里ECS云服务,1核2G1M宽带 40G SSD硬盘, 9 9一年,189二年,279三年 23
最热文章
Elasticsearch教程(四) elasticsearch head 插件安装和使用 117106
Elasticsearch教程(六) elasticsearch Client创建 75842
Elasticsearch教程(八) elasticsearch delete 删除数据(Java) 72354
Elasticsearch教程(二),IK分词器安装 70206
免费天气API,全国天气 JSON API接口,可以获取五天的天气预报 63987
Elasticsearch教程(一),全程直播(小白级别) 57778
Elasticsearch教程(五) elasticsearch Mapping的创建 55727
Elasticsearch教程(三),IK分词器安装 (极速版) 55122
Elasticsearch教程(七) elasticsearch Insert 插入数据(Java) 50391
Elasticsearch权威指南-中文.pdf,Elasticsearch 中文文档下载 43310

骚码加入我们 / 千人QQ群:259217951

入群需要5元,如果没有QQ钱包,可以先Alipay、微信,赞助然后加群主拉进。

二维码生成 来自 >> 二维码生成器

支付扫码

所有赞助/开支都讲公开明细,用于网站维护:赞助名单查看

正在加载... ...