bigywater.tech
Elasticsearch · 15

_source、stored_fields 与 docvalue_fields

真实关闭_source并给字段单独开store,验证stored_fields和docvalue_fields如何在没有_source的情况下取到数据,以及Update API为什么会因此彻底失效。

关闭_source后GET
真实found=true但完全没有内容
stored_fields / docvalue_fields
真实能独立于_source取到字段值
Update API
真实直接报document_source_missing_exception

做的事:真实关闭_source,给title字段单独开store,看哪些取值方式还能用

代码PUT /nosource_test {"mappings": { "_source": {"enabled": false}, "properties": { "title": {"type": "keyword", "store": true}, "views": {"type": "integer"} } }} POST .../_doc/1 {"title":"hello", "views":42} GET .../_doc/1 # 普通取文档 GET .../_search {"stored_fields":["title"]} # 走store GET .../_search {"docvalue_fields":["views"]} # 走doc_values POST .../_update/1 {"doc":{"views":43}} # 能更新吗

验证:关掉_source后,普通GET真实"找到了"文档但完全没有内容

实测 · 本机 Elasticsearch 8.15.0 执行输出
GET /nosource_test/_doc/1
→ {"_index":"nosource_test","_id":"1","found":true}
  (真实没有任何_source字段,连title和views的值都看不到)

这是关闭_source最直接的真实后果:found:true说明这条文档真实存在,但响应里真实完全没有数据本身——_source存的是写入时那份完整的原始JSON备份,关掉它之后,这份"万能备份"真实不再存在,默认的取值方式完全拿不到任何字段内容。

验证:stored_fields和docvalue_fields真实提供了两条独立于_source之外的取值路径

实测 · 本机执行输出
GET .../_search {"stored_fields":["title"]}
→ fields.title = ["hello"]      ← 真实拿到了,走的是title字段单独开的store:true

GET .../_search {"docvalue_fields":["views"]}
→ fields.views = [42]            ← 真实也拿到了,走的是views字段默认开启的doc_values
这组结果真实证明了关闭_source不等于数据真的丢了——只要某个字段真实开了store:true,这个字段的值就真实在Lucene层面单独存了一份,stored_fields参数能把它取出来;views这个普通integer字段默认doc_values就是开启的(本系列前面验证过的列式存储),docvalue_fields参数真实能直接从这份列式存储里读出值。这解释了为什么有些真实场景会故意关掉_source:当文档体积很大、但大部分字段的原始值不需要被完整取回(只需要搜索、聚合、或者取少数几个关键字段)时,_source这份完整备份真实是一笔不小的存储开销(通常是原始数据的两倍左右,Lucene要同时存一份倒排索引和一份原文),关掉它再给确实需要取值的字段单独开store,真实能省下这部分冗余空间。

真实致命的代价:没有_source,连最简单的partial update都做不到

实测 · 本机执行输出
POST /nosource_test/_update/1 {"doc":{"views":43}}
→ 400 document_source_missing_exception: "[1]: document source missing"

这是本文认为关闭_source最真实的代价所在:ES的Update API真实不是在Lucene层面原地修改某个字段(Lucene的段文件本身真实是不可变的,本系列后面会详细验证这一点),而是真实先读出这条文档完整的_source,在内存里跟传入的doc合并成新版本,再整条重新写入——这个"读出旧_source再合并"的步骤真实完全依赖_source的存在,没有它,Update API从机制根源上就真实没法工作,哪怕只是想改一个字段的值。这也是为什么关闭_source通常只适合那些写入后不再需要原地更新、只需要只读查询的数据场景(纯日志、不可变的归档数据),一旦业务真实需要partial update,关闭_source这条路真实走不通。

分步演示:从_source的完全缺失到两条替代路径再到update的失效

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

关闭_source后,三条取值路径和update的真实生死
这一步新验证的取值方式

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 _source.enabled / stored_fields / docvalue_fields
    京ICP备2026067502号-1京公网安备11010502063487号