2.2 第一条文档的往返:从写入到检索 本节摘要:这是全书的微缩预演——建索引、写文档、按 id 取、按词搜、删文档,五步走完 doc-1001 的第一次往返。每一步都配上"引擎里发生了什么"的注解:分片怎么定、倒排表怎么长、查询怎么命中。后面七章不过是把这五步分别放大,读到这里,旅程的骨架就已经在你手上。 完整跑一遍往返 上一节装好的环境现在登场。所有请求都可以在 Kibana 控制台直接运行,注释用井号开头。 往返全景 往返全景 第一步:建索引 先给工单们安个家。主分片数 3、副本 0(单节点副本无处安放): 返回里只有确认标志。此时引擎在数据目录里建好 3 个分片文件夹,每个分片是一个空着的 Lucene 索引,等着第一批词条。为什么现在敢设 3 个分片?
本节摘要:这是全书的微缩预演——建索引、写文档、按 id 取、按词搜、删文档,五步走完 doc-1001 的第一次往返。每一步都配上"引擎里发生了什么"的注解:分片怎么定、倒排表怎么长、查询怎么命中。后面七章不过是把这五步分别放大,读到这里,旅程的骨架就已经在你手上。
上一节装好的环境现在登场。所有请求都可以在 Kibana 控制台直接运行,注释用井号开头。

先给工单们安个家。主分片数 3、副本 0(单节点副本无处安放):
PUT tickets { "settings": { "number_of_shards": 3, "number_of_replicas": 0 } }
返回里只有确认标志。此时引擎在数据目录里建好 3 个分片文件夹,每个分片是一个空着的 Lucene 索引,等着第一批词条。为什么现在敢设 3 个分片?按第 1 章的纪律,主分片数建后不可改,3 是学习与小型生产的折中——真正的容量推演见第 9 章。
PUT tickets/_doc/doc-1001 { "title": "用户申请退款,处理速度太慢", "status": "pending", "priority": 2, "tags": ["退款", "时效"] }
{ "_index": "tickets", "_id": "doc-1001", "result": "created", "_shards": { "total": 1, "successful": 1, "failed": 0 }, "_seq_no": 0, "_primary_term": 1 }
请求发出的瞬间,协调节点算出 hash(doc-1001) 对 3 取余等于 1,把它转给 1 号主分片。分片上的分析器把标题拆成词条,倒排表里从此多了"用户、申请、退款、处理、速度、太慢"六个词条的登记。因为我们显式给了文档 id,重复执行这条请求是覆盖语义——同 id 再写一次,result 变成 updated,旧版本被打上删除标记。
字段类型此时被动态映射推断:title 变成 text 加一个 keyword 子字段,priority 变成整数,tags 变成文本数组。学习期这样省事,生产上要把动态映射关掉,改用显式声明——推断可能出错(把日期格式的单号推成日期类型),而且错了就要重建。
GET tickets/_doc/doc-1001
响应的 _source 字段里是原封不动的 JSON 原文。注意引擎存了两份信息:倒排表负责"搜得到",_source 负责"取得回"。这种按 id 的点查同样走路由公式,直达 1 号分片,不经过任何扫描。
见证倒排索引兑现承诺的时刻:
GET tickets/_search { "query": { "match": { "title": "退款" } } }
{ "hits": { "total": { "value": 1, "relation": "eq" }, "hits": [ { "_id": "doc-1001", "_score": 0.2876821, "_source": { "title": "用户申请退款,处理速度太慢" } } ] } }
查询词"退款"先被同一个分析器切成词条,再去 3 个分片各自的倒排表里查——每个分片独立返回本地命中与打分,协调节点归并出全局排序。那个 0.28 的分数是相关性打分的产物,第 5 章会拆开它的公式。
⚠️ 常见坑:写入立刻搜索却查不到?不是丢了。文档要先经历 refresh(默认每秒一次)才从内存缓冲区进入可搜索的段。写完等一秒再查,或手动调刷新接口——背后的三段机制在 8.3 节有完整拆解。
DELETE tickets/_doc/doc-1001 # result: deleted PUT tickets/_doc/doc-1001 ... # 同id再写 即为重建
删除不是立刻抹掉词条,而是写入一个"墓碑"标记,读取时过滤,空间在段合并时回收。所以频繁写删的索引会看到磁盘回收滞后,这是正常行为,不是泄漏。
| 考核点 | 达标标准 |
|---|---|
| 五步闭环 | 不看资料独立完成建索引、写、取、搜、删 |
| 引擎内幕 | 每一步说出路由、分词、倒排、刷新中的哪个机制在起作用 |
| 动态映射 | 说出 title 首次写入被推断成的类型,以及生产关闭推断的理由 |
| result 字段 | 区分 created、updated、deleted 三种值的语义与触发条件 |
| 可见性两层 | 解释按 id 取立即可得、按词搜要等一秒的根源差别 |
在环境里独立完成这组动作,每步先写下预期结果再执行:写入一条新工单(预期 result 为 created);立刻按 id 取(预期 found 为 true);立刻按词搜标题(预期可能为空,等一秒再搜预期命中);用同 id 重写(预期 result 变 updated);删除后再取(预期 found 为 false)。五步预期全部命中,说明这一节的机制真的进了脑子,而不是眼睛看过——写预期这一步不能省,它把"看懂"与"会"区分开。
为什么搜索返回的 total 是精确值而有时又标注 gte?为什么 _score 有时候是 null?这些疑问会在第 5 章逐一回收。眼下更重要的是复述能力:合上书,把五步与每步的引擎内幕说一遍——说得出来,这本书的骨架就已经长在你身上了。
试验场跑通,下一站进入引擎内部:分词台。doc-1001 将被拆开、登记,以词条的身份住进倒排索引。