ES 分词器与中文分词

ES 分词器与中文分词

候选人小周在面试时被问到:"你们搜索'北京理工大学'能搜到吗?"

小周说:"能搜到啊。"

面试官追问:"那搜索'北京'呢?'理工'呢?'大学'呢?"

小周愣了一下:"应该都能搜到?"

面试官拿起电脑,说:"我给你跑个 demo 看看。"

结果发现:搜索"理工大学"能搜到,搜索"理工"一个字都搜不出来。

小周当场被问住了。

【面试官心理】 这道题我是在测试候选人对分词器的理解深度。很多候选人知道 ES 要分词,但不知道分词策略会直接影响召回率和精确率。这个"搜不出来"的坑,90% 用 ES 做中文搜索的团队都踩过。


一、分词器三大组件 🔴

1.1 组件拆解

ES 的分词器由三个组件串联而成:

原始文本


Character Filter(字符过滤器)
   │ ① 去除 HTML 标签
   │ ② 转换特殊字符(& → and)


Tokenizer(分词器)
   │ 按规则切分词条


Token Filter(词条过滤器)
   │ ① 转小写(英文)
   │ ② 同义词替换
   │ ③ 去除停用词


词条列表(Token Stream)

1.2 常用分词器对比

分词器切分策略适用语言优点缺点
standard按空格和标点切分 + 小写化英文默认,简单中文按字切
simple按非字母字符切分英文去掉数字和符号中文单字
whitespace按空格切分英文最简单不处理标点
keyword不分词全部原样保留无法搜索
ik_max_word词典最大切分中文召回率高分词过多
ik_smart词典智能切分中文分词精准召回率低
hanlpCRF/HMM中英日韩词性标注依赖模型

1.3 自定义分词器示例

PUT /products
{
  "settings": {
    "analyzer": {
      "my_analyzer": {
        "type": "custom",
        "char_filter": ["html_strip"],
        "tokenizer": "ik_max_word",
        "filter": ["lowercase", "synonym_filter"]
      }
    },
    "filter": {
      "synonym_filter": {
        "type": "synonym",
        "synonyms": [
          "iPhone, 苹果手机",
          "电脑, 计算机, PC"
        ]
      }
    }
  },
  "mappings": {
    "properties": {
      "product_name": {
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}

二、中文分词三大流派 🟡

2.1 词典匹配派 — IK

IK 是国内最流行的 ES 中文分词器,基于词典的前向最大匹配和后向最大匹配。

ik_max_word:穷举式切分,给出所有可能的组合。

输入:北京理工大学

ik_max_word 输出:
["北京", "北京理工", "北京理工大学", "理工", "理工大学", "理工大", "工大", "大学", "校"]

ik_smart:智能切分,给出最合理的组合。

输入:北京理工大学

ik_smart 输出:
["北京", "理工", "大学", "校"]
💡

选 ik_smart 还是 ik_max_word?

  • ik_smart:精确搜索场景(如搜索框),用户输入短词,追求精确率
  • ik_max_word:召回搜索场景(如搜索引擎),用户输入模糊,追求召回率

对于电商搜索,建议用 ik_smart 作为主分词器,再用 ik_max_word 建一个子字段用于高召回:

{
  "properties": {
    "product_name": {
      "type": "text",
      "analyzer": "ik_smart"
    },
    "product_name_ext": {
      "type": "text",
      "analyzer": "ik_max_word"
    }
  }
}

2.2 统计模型派 — HanLP / Jieba

HanLP 基于 CRF(条件随机场)和 HMM(隐马尔可夫模型),通过统计大量语料学习词边界。

优势

  • 能识别新词(未登录词),如"内卷"、"躺平"
  • 支持词性标注、命名实体识别(NER)
  • 英文和中文字符混合切分效果更好

劣势

  • 需要加载模型文件,部署复杂
  • 首次加载慢,内存占用大

2.3 分词粒度对搜索的影响 🔴

这是面试中最高频的深水区。

召回率 vs 精确率

分词策略召回率精确率适用场景
单字分词最高最低无(噪声极大)
ik_smart中等精确搜索
ik_max_word中等全文搜索
自定义词典可控可控垂直领域
⚠️

最常见的翻车场景

// 错误配置:text 字段没有指定分词器
{
  "properties": {
    "description": { "type": "text" }  // 使用默认 standard,中文按字切
  }
}

搜索"北京"能搜到"北京理工大学",但搜索"理工大学"一个字都搜不出来。因为 standard 分词器把中文按单字切了:

"北京理工大学" → ["北", "京", "理", "工", "大", "学"]
搜索"理工" → 在倒排索引中找"理" AND "工" 的交集 → 搜不到

这个坑我在线上看过至少 10 次,团队排查了 3 天,最后发现就是 mapping 里忘了配中文分词器。


三、自定义分词器实战 🟡

3.1 停用词配置

中文停用词("的"、"了"、"和")在倒排索引中占据大量空间,但搜索价值极低。配置停用词可以:

  1. 减小索引体积:减少 15%~30% 的索引大小
  2. 提升查询性能:跳过无意义的词条
  3. 减少噪音结果:不会匹配到全是"的"的结果
{
  "filter": {
    "chinese_stop": {
      "type": "stop",
      "stopwords": ["的", "了", "和", "是", "在", "有", "我", "就", "不", "也"]
    }
  }
}

3.2 同义词配置

同义词是搜索体验的关键环节。配置不当会导致:

  • 用户搜"苹果手机"搜不到"iPhone"(差评来源 Top 1)
  • 用户搜"电脑"搜不到"计算机"或"PC"
{
  "filter": {
    "product_synonym": {
      "type": "synonym",
      "synonyms": [
        "iPhone, 苹果手机, 苹果",
        "电脑, 计算机, PC, 笔记本",
        "手机, 移动电话, 智能机"
      ]
    }
  }
}
📖 点击展开:同义词的坑

同义词展开有两种策略:

1. 索引时展开(Index Time Synonym)

  • 索引阶段就把"iPhone"和"苹果手机"存到同一个词条
  • 查询时不需要额外处理
  • 缺点:修改同义词后需要重建索引

2. 查询时展开(Search Time Synonym)

  • 索引时原样存储
  • 查询时动态展开同义词
  • 缺点:每次查询都要做同义词转换

最佳实践:使用 Search Time Synonym,配置 synonyms_set,这样修改同义词后不需要重建索引。


四、错误示范

❌ 错误示范 1

候选人原话:"我把 description 字段设成了 keyword 类型,这样搜索就快了。"

问题诊断

  • keyword 字段不会分词,只能做精确匹配
  • 搜索"手机"搜不到"苹果手机"
  • 完全混淆了"查询快"和"搜索有效"的区别

❌ 错误示范 2

候选人原话:"中文分词用 standard 就行,它会自动处理中文。"

问题诊断

  • standard 只识别空格和标点,中文按字切
  • "北京理工大学"被切成 6 个单字
  • 说明候选人没有实际测试过中文搜索效果

面试官内心 OS:"这两个候选人都是典型的'用过但没踩过坑'。他们可能觉得配置都对了,但从来没在线上验证过搜索结果的质量。直到用户投诉'搜什么都搜不到'才反应过来。"


五、生产避坑

5.1 分词器版本不一致翻车

线上后果:线下环境搜索正常,上线后搜索结果完全不对。

根因:线下和线上用了不同版本的 IK 分词器,词库差异导致分词结果不同。某些词在测试环境被正确切分,线上却被切成了另一个意思。

解决方案

  • 所有环境使用相同版本的分词器 jar 包
  • 将自定义词典和同义词配置放到 config 目录的公共位置
  • 上线前用固定的测试用例验证分词结果

5.2 自定义词典加载时机

线上后果:更新 IK 词典后,ES 重启前新词不生效,导致 3 天内的搜索都有遗漏。

根因:IK 的自定义词典在启动时加载,运行期间不会热更新。

解决方案

# IK 词典热更新接口(需要 IK 插件支持)
POST /_ik hotReloadDict

或者使用动态同义词配置(synonyms 参数支持从文件加载,reload 时自动读取)。


六、工程选型

场景分词器推荐配置策略
电商商品搜索ik_smart + ik_max_word 双字段高精确 + 高召回
新闻资讯搜索hanlp词性标注、命名实体识别
日志关键词检索standard + lowercase大吞吐量,精度要求低
法律/医疗等专业领域hanlp + 自定义词典专业词库 + 术语标准化
拼音搜索pinyin + ik_smart拼音首字母和全拼

【面试官心理】 这道题我能看出候选人有没有被用户投诉"搜不到"折磨过。被投诉过的候选人,一定会去研究分词器的每一个参数,会去配同义词词典,会去测召回率。这种候选人,才是真正在 ES 上花了心思的。