土法炼钢兴趣小组的算法知识备份

【全文检索引擎】分析链 Analyzer:从文本到 TokenStream

文章导航

分类入口
databasestorage
标签入口
#lucene#analyzer#tokenizer#tokenfilter#charfilter#tokenstream#chinese-segmentation#analysis-chain

目录

Lucene 文档模型 里,TextField 写入倒排前必须经过 Analyzer:同一篇中文正文,用 StandardAnalyzer 与用 ICU 或 IK 插件切出来的 term 集合不同,进而改变 FST 词典规模、postings 长度与召回行为。运维 ES 时改 analyzersearch_analyzer 往往要 reindex——根源就在分析链与段文件绑死,而非单纯改配置即生效。

本文是系列第 3 篇,回答:

  1. CharFilter、Tokenizer、TokenFilter 各改什么?
  2. TokenStream 契约为何约束索引与查询两侧必须一致?
  3. 中文分词在引擎层只需掌握哪些接口与代价,而不必成为 NLP 专家?

版本锚定:Apache Lucene 9.x/10.x Analysis 包官方文档;Elasticsearch 8.x AnalysisReindex 说明。不写 jieba / IK 内部词典算法;插件行为以对应发行版文档为准。


一、最小故事:同一标题,两种 analyzer,两个索引

字符串:「Elasticsearch倒排索引」(无空格中文 + 英文粘连)

阶段 Standard 路径(简化) 中文插件路径(概念)
Tokenizer 可能切出 elasticsearch倒排索引 或更碎 词典驱动:Elasticsearch倒排索引
查询 倒排 能否命中取决于索引侧是否出现同 term 同左——索引与查询 analyzer 必须对齐
词典 term 数 可能更多碎词 较少长词,单 term postings 变长

若索引用 ik_max_word、查询用 standard,会出现 搜不到已入库文档 的经典故障——不是倒排损坏,而是 term 空间不一致。第 17 篇排障会回链本节。


二、分析链三阶段

Lucene 官方 Analysis 文档将 Analyzer 拆为(A 级):

flowchart LR
  raw["Raw text"]
  cf["CharFilter chain"]
  tok["Tokenizer"]
  tf["TokenFilter chain"]
  terms["Term stream to IndexWriter"]
  raw --> cf --> tok --> tf --> terms

2.1 CharFilter:字符流预处理

字符流 上操作,可改长度(如折叠 fi)。常见:

多个 CharFilter 串行;输出进入 Tokenizer。

2.2 Tokenizer:切分基准单元

产生 初始 token,带 offsetpositionIncrement 等属性。例如:

Tokenizer 有且只有一个

2.3 TokenFilter:token 级变换

在 token 流上过滤或变换,可删 token、改词形、注入同义词。常见:

多个 TokenFilter 串行;最终流交给 IndexWriter 写 postings。

flowchart TB
  subgraph indexTime ["Index time analyzer"]
    i1["CharFilter"]
    i2["Tokenizer"]
    i3["LowerCase + Stop"]
    i1 --> i2 --> i3
  end
  subgraph searchTime ["Search time analyzer"]
    s1["Same Tokenizer base"]
    s2["Maybe SynonymGraph"]
    s1 --> s2
  end
  indexTime --> inv["Inverted index"]
  searchTime --> qry["Query parser terms"]

三、TokenStream 契约

TokenStream 继承 AttributeSource,核心属性包括(Lucene TokenStream API):

属性 含义
CharTermAttribute 词项文本(写入词典的 bytes)
OffsetAttribute 原文字符起止(高亮)
PositionIncrementAttribute 相对前一 token 的位置步进
PositionLengthAttribute Graph token 跨度(同义词)
TypeAttribute 可选类型标签

契约

  1. 消费者调用 reset()incrementToken() 循环 → end()close()
  2. 索引与搜索对同一逻辑字段应使用 兼容 的分析结果(通常同一 Analyzer 实例或显式 search_analyzer)。
  3. positionIncrement=0 表示同位置多 token(同义词图);短语查询需正确消费 Graph(Lucene QueryParser / ES match_phrase 文档)。

违反契约的症状:能 single-term 搜到、phrase 搜不到;或 highlight 偏移错乱。


四、写放大与词典代价

每个 token 至少产生一个 (field, term) 倒排项。设文档 \(d\) 中字段 \(f\) 经分析得到 \(t_{d,f}\) 个 token,则单次 addDocument 对倒排的插入量约为:

\[ W_{\text{postings}} \approx \sum_{f \in \text{indexed}} t_{d,f} \]

再乘上 IndexOptions 决定的 freq/position/offset 倍数(第 5 篇)。因此:

策略 词典 召回 风险
细粒度分词 噪声、写放大
粗粒度 / keyword 漏召回
索引细 + 搜索粗 需 custom 管道 折中 配置复杂

五、中文分词:引擎接口与运维,不是 NLP 教程

中文无空格分隔,Tokenizer 必须依赖 词典、统计或模型。Lucene 本体不提供生产级中文分词;常见做法:

本系列只掌握引擎层四点(A 级边界 + 工程判断):

  1. 插件作为 Tokenizer/TokenFilter 接入,输出仍是标准 TokenStream
  2. 词典更新不自动改写历史 Segment;要一致召回需 reindex 或新索引别名切换(ES Reindex)。
  3. 多义词与专有名词导致的切分歧义,体现为 term 集合不同,不是 SQL COLLATE 能修。
  4. 性能:分词在 flush 路径上 CPU 密集;bulk 写入时 analyzer 复杂度直接影响 indexing 吞吐——具体数字取决于硬件与插件,本站不在无实测环境下给出口径。

与 PG 对比:GIN tsvector 使用内置 to_tsvector 词典配置;改配置同样涉及重建索引,语义与 Lucene analyzer 变更类似。

争论(工程边界):「通用中文分词应统一国标 / 统一模型」vs「业务定制词典不可少」——学术 NER/分词论文优化的是 F1,搜索引擎优化的是 term 一致性与写放大;生产以 可复现 pipeline + 版本化词典 为准,而非追逐单一 SOTA 模型(预印本算法不能单独支撑「业界已统一」断言)。


六、自定义 Analyzer 示例(Java)

与 Lucene 9.x 兼容的装配方式;本地需 lucene-analysis-common 等依赖。

import org.apache.lucene.analysis.*;
import org.apache.lucene.analysis.standard.StandardTokenizer;
import org.apache.lucene.analysis.core.LowerCaseFilter;
import org.apache.lucene.analysis.core.StopFilter;
import org.apache.lucene.analysis.en.EnglishAnalyzer;
import org.apache.lucene.util.CharArraySet;

StandardTokenizerFactory tokFactory = new StandardTokenizerFactory(new HashMap<>());
Tokenizer tokenizer = tokFactory.create();
TokenStream stream = new LowerCaseFilter(tokenizer);
stream = new StopFilter(stream, EnglishAnalyzer.ENGLISH_STOP_WORDS_SET);

// IndexWriterConfig.setAnalyzer(new Analyzer() { ... wrap stream ... });

Elasticsearch 侧等价物是 custom analyzer 定义(tokenizer + filter 列表),由节点加载插件提供 factory 类名。

6.1 Elasticsearch mapping 中的双 analyzer

生产常用 index 与 search 分离(ES 8.x Reference Analyzer):

{
  "settings": {
    "analysis": {
      "analyzer": {
        "idx_analyzer": {
          "tokenizer": "standard",
          "filter": ["lowercase"]
        },
        "srch_analyzer": {
          "tokenizer": "standard",
          "filter": ["lowercase", "synonym_graph_filter"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "idx_analyzer",
        "search_analyzer": "srch_analyzer"
      }
    }
  }
}

索引侧保持细粒度 term;查询侧在 Graph 上展开同义词,避免 postings 写放大。修改 analyzer 字段后,旧段 term 空间不变——必须 reindex 或建新索引别名切换,这与 Lucene 段不可变一致。

6.2 复现:对比两种 analyzer 的 term(步骤)

本站未粘贴本机输出;读者可在单节点 ES 8.x 或 Lucene Analyzer 单元测试中复现:

# ES:查看某字段分析结果(需已创建索引)
curl -s -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d '{
  "analyzer": "standard",
  "text": "Elasticsearch倒排索引"
}'
# 将 analyzer 换为 icu_tokenizer / 已安装的中文插件,对比 tokens 数组

Lucene 纯库路径:对同一 StringReader 分别 analyzer.tokenStream("f", reader),打印 CharTermAttribute,比较 token 列表差异。


七、与后续篇目的衔接

sequenceDiagram
  participant IW as IndexWriter
  participant An as Analyzer
  participant TS as TokenStream
  participant PF as PostingsConsumer
  IW->>An: tokenStream(field, reader)
  An->>TS: CharFilter/Tokenizer/Filters
  loop incrementToken
    IW->>TS: next token
    TS-->>PF: term + pos + offsets
  end
  PF->>PF: flush block codec

八、常见误解

  1. 「换 analyzer 只影响新文档」。旧 Segment 仍用旧 term;除非 reindex,否则同一字段两套 term 空间共存,查询行为不可预测。
  2. 「同义词库越大越好」。索引侧同义词使 \(W_{\text{postings}}\) 线性膨胀,且干扰 BM25 -idf 统计(第 6 篇)。
  3. 「中文分词靠查询侧智能就行」。查询再智能,索引侧没切出 term 仍无法倒排 seek。

九、小结

三句话小结

  1. Analyzer 是 CharFilter → Tokenizer → TokenFilter 的确定性管线,产出 TokenStream 驱动倒排写入。
  2. 索引与搜索 analyzer 不一致是生产最常见「明明有数据却搜不到」的原因之一。
  3. 中文只需盯住 插件接口、词典版本、reindex 代价;算法细节留给 NLP 团队,引擎团队盯住写放大与 term 一致性。

参考资料

规范 / 文档

  1. Apache Lucene 9.x/10.x, Analysis overview、TokenStreamAnalyzer
  2. Elasticsearch 8.x Reference, AnalysisSynonym token filterReindex data
  3. ICU Analysis integration(Lucene 官方模块文档)。

站内

  1. 文档模型FST 词典PG GIN

上一篇:Lucene 文档模型 | 下一篇:词项词典 FST系列目录

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .