3.2 分析器解剖:字符过滤、分词与归一化 本节摘要:分析器(analyzer)是把文本变成词条的流水线,分三段:字符过滤器先清洗原文、分词器切出词条、词条过滤器再归一化。本节用分析接口当显微镜,亲眼看标准分析器把中文切成单字的真相,然后组装一个自定义分析器处理真实脏数据。搜索质量的上限由这一节决定——写入时切不出的词,查询时永远搜不到。 把一段话放进化验室 引擎提供了分析接口,任何文本扔进去都能看到切词结果。这是全书性价比最高的调试工具: 真相揭晓:标准分析器对中文按单字切分。倒排表里登记的是"退"和"款"两个单字词条,不是"退款"这个词。后果两面性:搜"退款"能命中(两个单字都在),但相关性与噪声都有问题——含"推迟款式"的文档同样两个单字全中。
本节摘要:分析器(analyzer)是把文本变成词条的流水线,分三段:字符过滤器先清洗原文、分词器切出词条、词条过滤器再归一化。本节用分析接口当显微镜,亲眼看标准分析器把中文切成单字的真相,然后组装一个自定义分析器处理真实脏数据。搜索质量的上限由这一节决定——写入时切不出的词,查询时永远搜不到。
引擎提供了分析接口,任何文本扔进去都能看到切词结果。这是全书性价比最高的调试工具:
POST _analyze { "analyzer": "standard", "text": "用户申请退款,处理速度太慢" }
{ "tokens": [ { "token": "用", "start_offset": 0 }, { "token": "户", "start_offset": 1 }, { "token": "申", "start_offset": 2 }, { "token": "请", "start_offset": 3 }, { "token": "退", "start_offset": 4 }, { "token": "款", "start_offset": 5 } ] }
真相揭晓:标准分析器对中文按单字切分。倒排表里登记的是"退"和"款"两个单字词条,不是"退款"这个词。后果两面性:搜"退款"能命中(两个单字都在),但相关性与噪声都有问题——含"推迟款式"的文档同样两个单字全中。中文要按词切分,需要额外的分词器插件(社区常用的中文分词组件,提供最细切分与智能切分两档),装好后指定分析器即可:
POST _analyze { "analyzer": "ik_max_word", "text": "用户申请退款,处理速度太慢" }
{ "tokens": [ { "token": "用户" }, { "token": "申请" }, { "token": "退款" }, { "token": "处理" }, { "token": "速度" }, { "token": "太慢" } ]
"退款"成为独立词条后,搜"退款"直接命中词条级倒排表,噪声文档出局。写入与查询必须用同一个分析器(或用一致的切词结果),否则写入切的是"退款"、查询切的是"退、款",两边对不上暗号。
分析器不是一根魔法棒,而是三段工位的流水线,每个工位都可以单独定制:

字符过滤器处理字符串级的问题:客服回复常粘贴网页片段,标签先剥掉;中英混排时全角字母数字转成半角,否则"refund"和"refund"是两个词条。分词器是唯一必选工位,决定切分粒度。词条过滤器做归一化:转小写、去停用词、英文词干化——running 与 run 折算成同一个词干,查询侧同理,两边就能相遇。
场景:工单的客服回复字段混有网页标签、全半角混杂、还有大量"的、了、是"这类无信息量的字。组装方案如下:
PUT tickets_v3 { "settings": { "analysis": { "char_filter": { "tag_wash": { "type": "html_strip" } }, "filter": { "my_stop": { "type": "stop", "stopwords": ["的", "了", "是", "在"] } }, "analyzer": { "reply_analyzer": { "type": "custom", "char_filter": ["tag_wash"], "tokenizer": "ik_max_word", "filter": ["lowercase", "my_stop"] } } } }, "mappings": { "properties": { "reply": { "type": "text", "analyzer": "reply_analyzer" } } } }
验证产线效果,直接点名刚注册的分析器:
POST tickets_v3/_analyze { "analyzer": "reply_analyzer", "text": "<p>已联系仓储,REFUND单号是8899,请查收的了</p>" }
输出词条(节选):已联系 仓储 refund 单号 8899 请查收 变化点:标签消失 全角英文转小写归一 的与了被停用
三段工位各司其职:标签没了、REFUND 归一成 refund、语气助词出局。搜"refund 单号"从此两边暗号一致。
背景:客服平台上线两周,运营反馈搜"退款慢"几乎没有结果,但系统里明明一堆这类工单。操作:先看映射——回复字段用标准分析器,中文按单字切;查分析接口,"退款慢"切出"退、款、慢"三个单字,理论上应该命中很多,实际几乎没有。再看数据:回复文本是"退款太慢"这类表述,切词后确实包含这些单字,但业务里大量工单同时含"款式推迟"等噪声,相关性排序把噪声顶到了前几页,真目标被淹没。根因是单字词条粒度太粗,打分无法区分语义。修复:按上文组装带中文分词插件的 reply_analyzer,重建索引迁移数据(3.3 的流程),查询字段分析器保持一致。结果:搜"退款慢"首条就是当天最相关的三条工单,长尾噪声消失。变式:如果搜不到的问题来自"同义词"(用户说"退货"工单写"退款"),在词条过滤器里加一组同义词词典,写入侧或查询侧展开皆可——查询侧展开更省重建成本。
常见坑:同义词表在写入侧展开后,词表更新需要重建整个索引才能生效;放查询侧则即时生效但每次查询多一步展开。词表预期会频繁变更时,选查询侧。
关键直觉:调试任何"搜不到"问题的第一动作,永远是分别对写入文本与查询词各跑一次分析接口,对比两边词条。九成的谜团在这一步现形。