在搜索引擎的实际应用中,用户输入的关键词往往与数据库中存储的文本存在差异。例如:
- 用户搜 “手机”,但商品名称写的是 “智能电话” 或 “iPhone”。
- 用户搜 “士多啤梨”,但库存里叫 “草莓”。
- 用户搜 “AWS”,但文章里写的是 “亚马逊云科技”。
如果没有同义词处理,这些相关的文档将无法被检索到(召回率低)。本文将详细介绍如何在 INFINI Easysearch 中配置和优化同义词功能,弥合用户意图与数据之间的鸿沟。
一、 同义词的两种工作模式 #
在 Easysearch 中,同义词处理本质上是一个 Token Filter(词元过滤器),它可以工作在两个阶段:
1. 索引阶段(Index-time) #
在数据写入时,直接将同义词写入倒排索引。
- 优点:查询性能略高(因为不需要实时计算)。
- 缺点:灵活性极差。一旦规则修改(例如新增“西红柿 => 番茄”),必须重建索引(Reindex)才能生效。
- 结论:不推荐,除非是极度静态的同义词规则。
2. 查询阶段(Query-time)—— 推荐 #
在搜索时,动态地将用户的查询词扩展为同义词。
- 优点:灵活。修改同义词规则后,只需刷新配置即可立即生效,无需重建索引。
- 缺点:查询时增加了少许计算开销(但在 Easysearch 的优化下通常可忽略)。
- 结论:生产环境的最佳实践。
二、 核心组件:synonym_graph
#
在早期的 Elasticsearch 版本中,我们使用 synonym 过滤器。但在处理多词同义词(Multi-word synonyms,如 “domain driven design” <=> “DDD”)时,旧版过滤器容易破坏短语查询的位置信息。
在 Easysearch 中进行查询阶段处理时,请务必使用 synonym_graph 过滤器。它能正确处理跨多个 Token 的同义词图谱。
三、 实战:配置同义词搜索 #
我们将构建一个电商搜索索引,实现“查询时同义词扩展”。
3.1 准备同义词规则文件 #
建议将同义词规则独立存储在文件中,便于维护。
在 Easysearch 配置目录的 config/analysis 文件夹下(如果没有则创建),新建文件 synonyms.txt:
# Solr 格式
# 1. 等价扩展(搜索任意一个词,都能搜到其他的)
番茄, 西红柿
手机, 移动电话, 智能手机
kfc, 肯德基
# 2. 单向映射(搜左边的,扩展成右边的;搜右边的,不包含左边的)
# 场景:搜 "水果" 时,希望展示 "苹果",但搜 "苹果" 不需要展示所有 "水果"
iphone, ipad, mac => apple
3.2 定义分析器与索引 #
我们需要定义一个包含 synonym_graph 的自定义分析器,并将其指定为字段的 search_analyzer。
PUT /shop_index
{
"settings": {
"analysis": {
"filter": {
"my_synonym_graph": {
"type": "synonym_graph",
"synonyms_path": "config/synonyms.txt",
"updateable": true
}
},
"analyzer": {
"my_synonym_search_analyzer": {
"tokenizer": "ik_smart",
"filter": [
"my_synonym_graph"
]
},
"my_index_analyzer": {
"tokenizer": "ik_max_word"
}
}
}
},
"mappings": {
"properties": {
"product_name": {
"type": "text",
"analyzer": "my_index_analyzer",
"search_analyzer": "my_synonym_search_analyzer"
}
}
}
}
3.3 写入测试数据 #
POST /shop_index/_doc/1
{ "product_name": "新鲜的西红柿" }
POST /shop_index/_doc/2
{ "product_name": "新款智能手机" }
3.4 搜索验证 #
# 用户搜索 "番茄"
GET /shop_index/_search
{
"query": {
"match": {
"product_name": "番茄"
}
}
}
结果解析:
- 用户输入 “番茄”。
search_analyzer处理:ik_smart将其分为 “番茄”。synonym_graph命中规则 “番茄, 西红柿”。- 查询被重写为
(product_name:番茄 OR product_name:西红柿)。 - 文档 1(包含“西红柿”)被成功召回。
五、 常见坑与最佳实践 #
5.1 分词器的配合 #
同义词过滤器通常放在 Tokenizer 之后。
- 英文:
tokenizer: standard->filter: lowercase->filter: synonym。注意大小写,如果在同义词规则里写了小写,一定要先做 lowercase。 - 中文:
tokenizer: ik_smart->filter: synonym。确保同义词文件里的词,能被 IK 分词器切分出来。例如,如果 IK 把 “奥迪A6” 切分成 “奥迪” 和 “A6”,而同义词规则写的是 “奥迪A6, 豪车”,则可能无法匹配。建议同义词规则使用简单的词汇。
5.2 避免同义词风暴 #
不要滥用同义词将通用词映射到大量具体词(例如 a,b,c,d,e,f...)。这会导致 Query 被扩展得非常长,严重拖慢搜索速度。
5.3 排序与高亮 #
当使用同义词扩展查询时,搜索结果中可能会出现不包含用户原始关键词的文档(搜“番茄”出了“西红柿”)。
- 高亮(Highlighting):Easysearch 的高亮功能通常能自动识别同义词并进行高亮。
- 评分(Scoring):如果希望原始词(番茄)的权重高于同义词(西红柿),可以使用 Boost 或使用
simple_query_string等支持权重的查询方式,或者在同义词规则中使用单向映射控制。
总结 #
在 INFINI Easysearch 中实现同义词搜索,关键在于选择 查询时(Query-time) 策略,并结合 synonym_graph 过滤器。





