Rust in Everything
基于 tantivy 0.27.0

模式与分词

写入主线的第一步是 Index::create_in_dir(path, schema),第二步是把文档交给 IndexWriter。在这两步之前,有四件事要先定下来:索引里有哪些字段、每个字段怎么处理(schema);一个值最后变成什么字节去查词典(Term);文档本身用什么类型传进来、怎么存进 doc store 又怎么取回来(Document);文本怎么切成词(tokenizer)。这一部分讲的就是这四件事。

代码在 src/schema/(9882 行,其中 document/ 4502 行)、src/tokenizer/(9111 行,但 ascii_folding_filter.rs 4066 行和 stop_word_filter/stopwords.rs 2319 行都是数据表,真正的逻辑不到 2800 行)和独立的 tantivy-tokenizer-api crate(src/lib.rs 180 行)。行数都按 tag 0.27.0 统计,含单元测试。

  1. Schema 与字段:SchemaBuilder 按添加顺序给字段编号;FieldType 有 12 个变体,0.27.0 新增 TieBreaker 和 Custom;TEXT、STRING、STORED、FAST、INDEXED 是类型层面的标志列表,| 组合时 indexing 取左边那个;schema 原样序列化进 meta.json,打开时按它重建。
  2. Term:Term 是「字段 + 类型码 + 值」,但词典里的键只有值;数字用大端字节加符号位翻转变成可排序的 8 字节,日期在倒排里截到秒,JSON 字段用 \x01 分隔路径、\x00 结束路径;文中打印了五种类型的真实字节。
  3. 文档:Document 和 Value 两个 trait 让任何类型都能直接被索引;默认的 TantivyDocument 是把所有值压进一个 Vec<u8> 的 CompactDoc;doc store 里每个文档是「字段数 + (字段号, 类型码, 值)…」,没设 STORED 的字段根本不写,取回来就是没有。
  4. 分词:Tokenizer、TokenStream、TokenFilter 在 tantivy-tokenizer-api 里,TextAnalyzer 把一条 filter 链装进一个 box;默认注册四个名字;schema 只存名字,没注册时写入在 commit 才报错、查询在解析时报错;default 对中文几乎不可用,tantivy-jieba 通过同一个 api crate 接进来。
flowchart TB
  sch["Schema"] --> fe["FieldEntry"]
  fe --> name["分词器名字"]
  name --> tm["TokenizerManager"]
  doc["Document"] --> val["ReferenceValue"]
  val --> ta["TextAnalyzer"]
  tm --> ta
  ta --> term["Term 字节"]
  term --> td["词典与倒排"]
  val --> store["doc store"]
  val --> fast["快速字段"]

字段值从 Document 出来以后分三路:建索引的经过分词变成 Term,进词典和倒排表(倒排表、词典);STORED 的原样序列化进 doc store;FAST 的进列存(快速字段)。三路各自读 schema,互不相干,所以一个字段可以只索引不存、只存不索引,或者三样都要。

💬 关联讨论

发起讨论
加载中...