如果说倒排索引是ES的"心脏",那分词器就是ES的"消化系统"------它决定了一句话被拆成什么样的"营养颗粒"(词项)存入索引。如果分词分得不好,搜索就搜不准。
分词器的工作过程并非简单的"按空格切分",它在内部严格分为 3 个顺序执行的组件:
第一阶段:字符过滤器(Character Filters)
这是预处理环节,在正式分词前对原始文本进行"清洗"。
-
作用:剔除无用字符或进行统一替换。
-
内置示例:
-
HTML Strip:去除<p>、<a>等HTML标签,提取纯文本。 -
Mapping:将&替换为and,或将全角数字(123)替换为半角(123)。
-
-
注意 :这里不改变词意,只做"净化"。
第二阶段:分词器(Tokenizer)
这是最核心 的环节。它将清洗后的文本,按照一定规则切分成一个个独立的 Token(词项/单元),并记录每个词在原文中的位置(Position)和偏移量(Offset)。
-
常见切分规则:
-
按空格/标点切分 (
whitespace/standard):适用于英文。 -
按单字切分 (
keyword):不分词,整个字段作为一个词,适用于邮编、状态码。 -
按语义切分 (
ik_smart/jieba):专用于中文,因为中文没有空格,必须依赖词典判断。
-
-
关键点 :Tokenizer 决定了最小的搜索粒度。切得太细(如"中华人民共和国"切成"中""华""人"),搜"民国"会误出;切得太粗(整词),搜"中华"又搜不到。
第三阶段:词项过滤器(Token Filters)
分词完成后,对生成的 Token 列表进行二次加工。这里可以串联多个过滤器,按顺序执行。
-
Lowercase(小写化) :将
Apple->apple,保证搜索时不区分大小写。 -
Stopword(停用词过滤) :剔除无意义的虚词,如中文的"的"、"了",英文的
the、a。这些词出现太频繁,会严重拉低搜索评分并占用磁盘。 -
Synonym(同义词扩展):当遇到"番茄"时,自动添加一个同义词 Token "西红柿"。这样用户搜"西红柿"也能命中包含"番茄"的文档。
-
Stemming(词干提取,英文专有) :将
running->run,cats->cat。将时态和复数统一为词根,提高召回率。
你必须要懂的两个"时机"(巨坑预警)
分词器在 ES 中应用在两个不同 的时间点,使用的可以是不同的分词器:
-
Index Time(索引时) :文档写入时,使用
index analyzer将文本切碎存入倒排索引。 -
Search Time(搜索时) :用户输入搜索词时,使用
search analyzer将查询词切碎。
最佳实践 :为了让搜索更智能,索引时通常细粒度 (尽可能多地切出词根),搜索时粗粒度(保持短语完整性)。如果两者用反了,就会导致"搜得到但不相关"或"相关但搜不到"。
实战举例:中文分词是怎么"猜"的?(以 IK 分词器为例)
英文分词简单,但中文是连续字符 ,机器不知道哪里断词。IK 分词器的工作原理是 "词典匹配 + 歧义消除":
-
词典(Dictionary) :内置一个庞大的词库(如
南京市,长江,大桥)。 -
切分过程:
-
输入:
"南京市长江大桥" -
智能模式(
ik_smart):从左向右扫描,优先匹配最长词,输出[南京市, 长江大桥]。 -
细粒度模式(
ik_max_word):穷尽所有可能,输出[南京市, 南京, 市长, 长江大桥, 长江, 大桥]。
-
-
避坑 :如果词典里没有"江大桥",IK 就会切分成
[南京市, 长江, 大桥]。所以词典热更新是中文搜索维护的重点。
总结对比(帮助记忆)
| 组件 | 英文类比 | 中文特例 |
|---|---|---|
| Character Filter | 去掉 <b> 标签 |
将中文全角逗号","替换为英文"," |
| Tokenizer(核心) | 按空格切 "Hello World" |
依赖词典切 "你好世界" |
| Token Filter | Running -> run |
"西红柿" 添加同义词 "番茄" |
如果分词效果不理想,该怎么办?
-
不用 ES 自带:中文务必安装 IK 或 HanLP 插件。
-
自定义词典 :将公司内部专有名词(如"王者荣耀"、"特斯拉Model3")加入 IK 的
main.dic。 -
使用
term查询验证 :可以通过_analyzeAPI 直接测试分词结果,确保索引和搜索的分词器一致。
bash
POST /_analyze
{
"analyzer": "ik_smart",
"text": "南京市长江大桥"
}