搜索补全与 Suggesters
两种搜索补全方案:(1)拼音分词器实现中文模糊补全;(2)Completion Suggester 实现毫秒级自动补全。
方案一:拼音分词器(中文场景)
应用场景
用户在搜索框输入:
- 汉字 → “张三” 匹配含"张三"的文档
- 全拼 → “zhangsan” 匹配含"张三"的文档
- 拼音首字母 → “zs” 匹配含"张三"的文档

安装
# https://github.com/infinilabs/analysis-pinyin
./elasticsearch-plugin install https://github.com/infinilabs/analysis-pinyin/releases/download/v9.3.4/elasticsearch-analysis-pinyin-9.3.4.zip核心策略:索引与搜索分析器分离
PUT /suggest
{
"settings": {
"analysis": {
"analyzer": {
"pinyin_analyzer": {
"tokenizer": "ik_max_word",
"filter": ["pinyin_filter"]
}
},
"filter": {
"pinyin_filter": {
"type": "pinyin",
"keep_full_pinyin": true,
"keep_joined_full_pinyin": true,
"keep_original": true,
"keep_first_letter": true,
"keep_separate_first_letter": true
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "pinyin_analyzer",
"search_analyzer": "ik_smart"
}
}
}
}| 参数 | 说明 |
|---|---|
analyzer |
索引时使用:IK 分词 → 拼音转换 → 生成多路 term |
search_analyzer |
搜索时使用:仅用 IK 分词,不走拼音 |
keep_full_pinyin |
保留全拼(zhang, san) |
keep_joined_full_pinyin |
保留连拼(zhangsan) |
keep_first_letter |
保留首字母(z, s) |
keep_original |
保留原文(张三) |
keep_separate_first_letter |
保留分开的首字母 |

🔬 深入原理:索引时 “张三” → term 列表
["张", "三", "zhang", "san", "zhangsan", "zs", "z", "s"]。搜索时用户输入 “张三” 用 IK 分词匹配 “张”/“三”,输入 “zhangsan” 也匹配到 “zhangsan” term。分离分析器防止搜索 “zhang” 时匹配到与拼音无关的文档。
效果演示


方案二:Completion Suggester(英文/关键词场景)
拼音方案基于反向索引(Term Dictionary),每次搜索遍历倒排表,延迟约 10-50ms。而 Completion Suggester 基于 FST(Finite State Transducer),在内存中构建有限状态机,延迟约 1-5ms,适合英文前缀补全和结构化关键词补全。
FST vs 倒排索引
| 维度 | Completion Suggester (FST) | 拼音分词 (倒排索引) |
|---|---|---|
| 原理 | 内存中 FST,前缀树遍历 | 倒排索引 Term 匹配 |
| 延迟 | 1-5ms | 10-50ms |
| 中文支持 | ❌ 不直接支持拼音 | ✅ 原生支持 |
| 存储 | 内存,需谨慎控制规模 | 磁盘(OS 页缓存) |
| 适用 | 英文、标签、用户名、品牌 | 中文商品名、人名、地址 |
基础用法
# Mapping
PUT /suggest_completion
{
"mappings": {
"properties": {
"title": { "type": "text" },
"suggest": {
"type": "completion"
}
}
}
}
# 写入数据
POST /suggest_completion/_doc/1
{
"title": "Elasticsearch Guide",
"suggest": {
"input": ["Elasticsearch", "ES", "Search Engine"],
"weight": 100
}
}
# 查询
POST /suggest_completion/_search
{
"_source": "title",
"suggest": {
"title-suggest": {
"prefix": "elas",
"completion": {
"field": "suggest",
"size": 10,
"skip_duplicates": true
}
}
}
}| 参数 | 说明 |
|---|---|
input |
输入词条(可多个同义词) |
weight |
权重,越高越靠前(可选) |
skip_duplicates |
跳过重复文本 |
fuzzy |
启用模糊匹配(容忍拼写错误) |
模糊匹配
POST /suggest_completion/_search
{
"suggest": {
"title-suggest": {
"prefix": "elastc",
"completion": {
"field": "suggest",
"fuzzy": {
"fuzziness": "AUTO",
"min_length": 3
}
}
}
}
}方案三:Context Suggester — 带上下文的补全
根据上下文(如分类、地理位置)过滤补全建议。
# Mapping(按分类过滤)
PUT /places
{
"mappings": {
"properties": {
"suggest": {
"type": "completion",
"contexts": [
{
"name": "category",
"type": "category"
}
]
}
}
}
}
# 写入
POST /places/_doc/1
{
"suggest": {
"input": ["星巴克"],
"contexts": { "category": ["咖啡", "餐饮"] }
}
}
# 查询(只返回餐饮类)
POST /places/_search
{
"suggest": {
"place-suggest": {
"prefix": "星",
"completion": {
"field": "suggest",
"contexts": {
"category": ["餐饮"]
}
}
}
}
}方案四:Edge N-Gram(灵活但占空间)
在索引时按字符前缀拆分,适合更灵活的补全场景。
PUT /autocomplete
{
"settings": {
"analysis": {
"filter": {
"edge_ngram_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
}
},
"analyzer": {
"edge_ngram_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "edge_ngram_filter"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "edge_ngram_analyzer",
"search_analyzer": "standard"
}
}
}
}| 方案 | 适用场景 | 推荐度 |
|---|---|---|
| 拼音分词 | 中文搜索框(支持汉字+拼音+首字母) | ⭐⭐⭐ 中文首选 |
| Completion Suggester | 英文/标签/用户名前缀补全 | ⭐⭐⭐ 英文首选 |
| Context Suggester | 带分类/地域过滤的补全 | ⭐⭐ 需要分类过滤时 |
| Edge N-Gram | 自定义匹配逻辑、需要中缀匹配 | ⭐ 灵活但占空间 |
常见陷阱
| 陷阱 | 说明 |
|---|---|
| 🚨 搜索时也用拼音分词器 | 搜索 “zhang” 可能匹配到含 “张” 但与 Zhang 无关的文档 |
🚨 keep_original: false |
纯汉字搜索无法命中,必须保留原文 |
| 🚨 IK 插件和拼音插件版本不匹配 | 两者版本必须与 ES 版本一致 |
| 🚨 Completion Suggester 不支持中文拼音 | FST 不做分词,拼音场景应用方案一 |
| 🚨 Completion input 不允许重复 | 同一文档的 input 数组不允许有重复值 |
| 🚨 Edge N-Gram 索引膨胀 | 每个词生成 N 个 term,磁盘和内存消耗大 |