取回来的文档里没有这个字段
主线位置:查询主线最后一步,
searcher.doc(addr)从文档存储解压一个块取出原文。STORED、INDEXED、FAST三个 flag 各管什么见 Schema 与字段,doc store 的格式见文档存储。
flowchart TB
doc["add_document"] --> inv["倒排 INDEXED"]
doc --> st{"STORED 吗"}
st -->|是| store[".store 原文"]
st -->|否| skip["不写"]
inv --> hit["能搜到"]
hit --> get["searcher doc"]
store --> get
get --> miss["没有 body"]
实验 crate:exp-tantivy/pita-lab,src/bin/p05_stored.rs。M2 Max、macOS 27、rustc 1.99.0,release。
现象
schema 照着 tantivy 的 basic_search 例子写:title 是 TEXT | STORED,body 是 TEXT。写一篇,查一个只在 body 里出现的词 skiff:
hits for `skiff`: 1
to_json: {"title":["The Old Man and the Sea"]}
get_first(body): None
snippet(body): ""
- 能搜到,说明
body进了倒排索引。 - 取回来的文档里只有
title,get_first(body)是None,不是空串。 - 拿
body做高亮,SnippetGenerator不报错,给出一个空片段。
原因
TEXT 只有「索引」这一个属性,STORED 是另一个 flag,要显式或上。写文档存储时,BinaryDocumentSerializer::serialize_doc 先过滤字段:
let stored_field_values = || {
doc.iter_fields_and_values()
.filter(|(field, _)| self.schema.get_field_entry(*field).is_stored())
};
没设 STORED 的值根本没写进 .store,searcher.doc 解出来的文档自然没有它。倒排表里也找不回原文:那里存的是分词、小写之后的词和位置,标点、大小写、被过滤掉的词都没了。
高亮那一行同理。SnippetGenerator::snippet_from_doc 从传入的文档里拼出这个字段的文本,文档里没有这个字段,文本就是空串,空串上选不出片段。它不检查字段是否 STORED,所以没有错误。
修法
给需要取回或高亮的字段加上 STORED:
let body = sb.add_text_field("body", TEXT | STORED);
schema 建好以后不能改(设计取舍),已有索引里的文档也没有原文可补,只能用新 schema 建一个新索引,从你自己的数据源重新写一遍。
不是每个字段都该存。STORED 的代价是 .store 变大、取文档时要解压更多字节。常见的做法是:
- 只用来搜索、从不展示的字段,只
TEXT; - 要展示或高亮的字段,
TEXT | STORED; - 结果页只需要一个 id、正文另有存储的,id 字段
STRING | STORED,取到 id 再回自己的库里查。
怎么检查
启动时(或写 schema 的单元测试里)把每个字段的属性打出来,对照你的取回和高亮代码:
for (_, entry) in schema.fields() {
println!("check {:<5} indexed={} stored={}", entry.name(), entry.is_indexed(), entry.is_stored());
}
check title indexed=true stored=true
check body indexed=true stored=false
代码里凡是 get_first(field) 或 SnippetGenerator::create(.., field) 用到的字段,FieldEntry::is_stored 都应该是 true;可以写成一条断言。对已经上线的索引,index.schema() 读的就是 meta.json 里的 schema,同样能查。
小结
TEXT只负责能搜到,不负责能取回;取回和高亮要STORED。- 没存的字段不写进
.store,searcher.doc取回的文档里没有它,get_first是None,snippet_from_doc给出空串,全程没有错误。 - schema 不能改,补
STORED要重建索引;启动时或测试里用is_stored()核对要取回的字段。