真实对比date和date_nanos两种字段类型,用script_fields揭穿_source原文回显掩盖的真相——date类型真实把亚毫秒精度悄悄截断了。
代码PUT /date_test {"mappings": {"properties": { "ts_ms": {"type": "date"}, "ts_ns": {"type": "date_nanos"} }}} POST /date_test/_doc/1 { "ts_ms": "2026-10-01T12:00:00.123456789Z", "ts_ns": "2026-10-01T12:00:00.123456789Z" } GET /date_test/_doc/1 # _source看到的是原始字符串吗 GET /date_test/_search # script_fields读取真正存储的精度
GET /date_test/_doc/1 _source.ts_ms → "2026-10-01T12:00:00.123456789Z" ← 看起来精度还在 _source.ts_ns → "2026-10-01T12:00:00.123456789Z" ← 看起来也一样
这是本文认为最容易造成误判的一个真实现象:_source字段真实存的是原始写入的JSON文档本身(一份未经处理的备份),不是字段被索引之后真正的内部表示——不管一个date类型字段内部真实把时间精度处理成了什么样,GET单条文档看到的_source永远真实是写入时的原文,这会让人误以为"数据完整保留了",但这跟这个值真正能被用来排序、聚合、范围查询时的精度,是两件真实不同的事。
script_fields真实读取到的内部存储值: ts_ms_epoch (毫秒) → 1790856000123 ← 对应 .123(纳秒部分被真实截断) ts_ns_epoch (纳秒部分) → 123456789 ← date_nanos真实完整保留了全部9位 ts_ms格式化输出 → "2026-10-01T12:00:00.123Z" ← 真实只剩毫秒 ts_ns格式化输出 → "2026-10-01T12:00:00.123456789Z" ← 真实完整纳秒
POST /date_test/_doc/2 {"ts_ms": "not a date"}
→ 400 document_parsing_exception:
"failed to parse date field [not a date] with format
[strict_date_optional_time||epoch_millis]"这条真实报错信息里的strict_date_optional_time||epoch_millis,正是date类型没有显式指定format时真实默认采用的解析规则——||表示"依次尝试这几种格式,只要有一种能解析成功就算数",strict_date_optional_time对应标准的ISO8601日期时间字符串,epoch_millis对应直接传一个代表毫秒数的数字。"not a date"真实两种格式都解析不了,ES在写入这一刻就真实拒绝了整条文档,而不是把它当成一个普通字符串存进去——这跟本系列上一篇验证的"显式Mapping会在写入时强制校验类型"是同一套机制在日期类型上的具体体现。
每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
script_fields 与 Painless 脚本读取内部存储值