ES 分词器彻底搞懂

22 May 2026 – wusfe · 3 min read

ES 分词器彻底搞懂(analyzer / tokenizer / filter)

"为什么搜 '南京市长江大桥' 会匹配到 '南京市长'?"

"为什么搜 'java' 能找到 'JavaScript'?"

"为什么搜同一个词,中文能搜到英文搜不到?"

答案都在一个组件里:分词器(Analyzer)。它是 ES 全文搜索最底层的基础设施——索引时怎么切词,搜索时就怎么匹配。本篇把它拆开揉碎讲清楚。

Analyzer 的三个零件

Analyzer = Character Filter → Tokenizer → Token Filter
              ↓                   ↓             ↓
         字符预处理            切割成词      对词做后处理

Character Filter:还没切,先洗

在分词之前对原始字符串做字符级别的转换。ES 内置三种:

// HTML Strip:去掉 HTML 标签
"<p>iPhone 15</p>" → "iPhone 15"

// Mapping:字符替换(比如把表情符号转成文字)
"iPhone 🙂" → "iPhone smile"

// Pattern Replace:正则替换
"call 139-1234-5678" → "call 13912345678"

Character Filter 可以串联,按顺序执行。

Tokenizer:动刀切词

把字符串切成一个个 token(词元)。这是分词的核心步骤——选错 tokenizer,后面全白干。

Tokenizer 行为 示例
standard 按词边界切,Unicode 标准算法 "iPhone 15 Pro"["iPhone", "15", "Pro"]
whitespace 按空白字符切 "iPhone 15 Pro"["iPhone", "15", "Pro"]
keyword 不切,原样保留 "iPhone 15 Pro"["iPhone 15 Pro"]
ngram 按字符 n-gram 切 "abc" min=2,max=2 → ["ab", "bc"]
edge_ngram 前 n-gram "abc" min=2,max=3 → ["ab", "abc"]
pattern 正则切分 "a,b;c" pattern=[,;]["a", "b", "c"]
ik_smart(IK 插件) 中文粗粒度切 "南京市长江大桥"["南京市", "长江大桥"]
ik_max_word(IK 插件) 中文细粒度切 "南京市长江大桥"["南京市", "南京", "市长", "长江大桥", "长江", "大桥"]

Token Filter:切完后加工

对 token 做增、删、改。

// lowercase:转小写
["iPhone", "15", "Pro"] → ["iphone", "15", "pro"]

// stop:去掉停用词
["the", "iphone", "15", "is"] → ["iphone", "15"]

// stemmer:词干提取(英文)
["running", "runs", "ran"] → ["run", "run", "ran"]

// synonym:同义词扩展
["iphone"] → ["iphone", "苹果手机"]

// unique:去重
["iphone", "15", "iphone"] → ["iphone", "15"]

组装一个完整的中文 Analyzer

PUT /products
{
  "settings": {
    "analysis": {
      "char_filter": {
        "my_char_filter": {
          "type": "mapping",
          "mappings": ["=> => ", "& => and"]
        }
      },
      "filter": {
        "my_stop": {
          "type": "stop",
          "stopwords": ["的", "了", "在", "是", "我", "有", "和", "就"]
        }
      },
      "analyzer": {
        "my_chinese_analyzer": {
          "type": "custom",
          "char_filter":  ["html_strip", "my_char_filter"],
          "tokenizer":    "ik_max_word",
          "filter":       ["lowercase", "my_stop"]
        }
      }
    }
  }
}

处理流程:

输入:"iPhone 15 Pro Max 在售,有折扣"

1. char_filter html_strip:不变(无 HTML)
2. char_filter my_char_filter:不变
3. tokenizer ik_max_word:["iPhone", "15", "Pro", "Max", "在售", "有", "折扣"]
4. filter lowercase:["iphone", "15", "pro", "max", "在售", "有", "折扣"]
5. filter my_stop:去掉"有" → ["iphone", "15", "pro", "max", "在售", "折扣"]

输出:["iphone", "15", "pro", "max", "在售", "折扣"]

索引时 vs 搜索时 Analyzer

最重要也是最容易踩坑的原则:索引和搜索必须用语义上一致的 analyzer,否则匹配不上。

PUT /products
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_max_word",       // 索引时:细粒度切
        "search_analyzer": "ik_smart"    // 搜索时:粗粒度切
      }
    }
  }
}

为什么这样做?

  • 索引时细切"南京市长江大桥" → 尽可能多地产生 term,覆盖更多搜索可能性
  • 搜索时粗切:用户输入 "南京市长江大桥" → 只提取最核心的 term,减少噪音匹配

这是中英文搜索的标准优化手段——索引求全,搜索求精。

用 Analyze API 调试

不知道某个字段的 analyzer 效果如何?直接调 API 看:

GET /products/_analyze
{
  "field": "title",
  "text": "iPhone 15 Pro Max 在售"
}

返回:

{
  "tokens": [
    { "token": "iphone",   "start_offset": 0, "end_offset": 6, "position": 0 },
    { "token": "15",       "start_offset": 7, "end_offset": 9, "position": 1 },
    { "token": "pro",      "start_offset": 10, "end_offset": 13, "position": 2 },
    { "token": "max",      "start_offset": 14, "end_offset": 17, "position": 3 },
    { "token": "在售",     "start_offset": 18, "end_offset": 20, "position": 4 }
  ]
}

每个 token 的位置、起止偏移一目了然。上线前养成用 _analyze 验证的习惯——搜索不准,从分词开始排查。

三种常见的中文分词器

分词器 内置 效果 推荐
IK 插件 专业中文词典 + 自定义词典 ⭐ 首选
jieba 插件 统计分词,处理新词能力强 备选
standard 内置 单字切分:"南京市"→["南","京","市"] ❌ 中文不推荐
NGram 内置 按字符 n-gram 切分 只用于自动补全

IK 是目前 ES 中文生态的事实标准。安装后配置自定义词典能解决大量分词不准的问题:

<!-- IKAnalyzer.cfg.xml -->
<properties>
    <entry key="ext_dict">custom/mydict.dic</entry>
</properties>
<!-- custom/mydict.dic -->
长江大桥
南京市
Pro Max
iPhone 15

总结

分词器是 ES 全文搜索的地基。三个零件分工明确:

Character Filter → 洗字符(去 HTML、统一符号)
Tokenizer       → 动刀切(选对切法最关键)
Token Filter    → 后处理(转小写、去停用词、同义词)

记住一句话:索引和搜索的 analyzer 不一致,是所有"为什么搜不到"问题的第一嫌疑人。