--- title: "Easysearch 同义词搜索功能实现详解" date: 2026-02-13 lastmod: 2026-02-13 description: "详解 Easysearch 同义词搜索功能配置与实现,对比索引时与查询时两种策略,深入讲解 synonym_graph 过滤器、分词器协配与常见优化techniques,帮助提升搜索召回率。" tags: ["同义词", "搜索优化", "分析器"] summary: "在搜索引擎的实际应用中,用户输入的关键词往往与数据库中存储的文本存在差异。例如: 用户搜 “手机”,但商品名称写的是 “智能电话” 或 “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 的同义词图谱。" --- 在搜索引擎的实际应用中,用户输入的关键词往往与数据库中存储的文本存在差异。例如: - 用户搜 **“手机”**,但商品名称写的是 **“智能电话”** 或 **“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`: ```latex # Solr 格式 # 1. 等价扩展(搜索任意一个词,都能搜到其他的) 番茄, 西红柿 手机, 移动电话, 智能手机 kfc, 肯德基 # 2. 单向映射(搜左边的,扩展成右边的;搜右边的,不包含左边的) # 场景:搜 "水果" 时,希望展示 "苹果",但搜 "苹果" 不需要展示所有 "水果" iphone, ipad, mac => apple ``` ### 3.2 定义分析器与索引 我们需要定义一个包含 `synonym_graph` 的自定义分析器,并将其指定为字段的 `search_analyzer`。 ```json 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 写入测试数据 ```json POST /shop_index/_doc/1 { "product_name": "新鲜的西红柿" } POST /shop_index/_doc/2 { "product_name": "新款智能手机" } ``` ### 3.4 搜索验证 ```json # 用户搜索 "番茄" GET /shop_index/_search { "query": { "match": { "product_name": "番茄" } } } ``` **结果解析**: 1. 用户输入 "番茄"。 2. `search_analyzer` 处理:`ik_smart` 将其分为 "番茄"。 3. `synonym_graph` 命中规则 "番茄, 西红柿"。 4. 查询被重写为 `(product_name:番茄 OR product_name:西红柿)`。 5. 文档 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` 过滤器。