文档首页/ 云搜索服务 CSS/ 最佳实践/ 数据搜索与分析/ 通过同义词过滤器与自定义词库实现语义扩展搜索
更新时间:2026-07-07 GMT+08:00
分享

通过同义词过滤器与自定义词库实现语义扩展搜索

通过将同义词组配置到自定义同义词词库,并结合dynamic_synonym同义词过滤器在写入与查询阶段对词语进行自动扩展,使任意同义词均能双向命中相关文档,最终实现搜索召回率大幅提升。

应用场景

在内容搜索、客服问答等场景中,用户可能会使用不同的词语表达同一意思。例如,文档记录的是“开心”,而用户搜索的是“高兴”,如果缺乏同义词机制,这篇文档将无法被召回,造成漏检。通过配置自定义同义词词库,并利用dynamic_synonym同义词过滤器,可在查询和写入阶段自动扩展同义词,从而实现跨词语的语义级搜索,显著提升搜索召回率。

典型应用:

  • 内容平台搜索:用户使用口语化词汇搜索时,能精准匹配书面化文档内容。例如,搜索“笔记本”可召回“便携式电脑”的文章。
  • 电商搜索扩展:商品描述使用正式名称,用户搜索时常用俗称或缩写。例如,搜索“手机”可召回描述为“移动终端”的商品。
  • 医疗问答系统:同一疾病或药物存在多种称谓,患者使用通俗名称也能检索到相关内容。例如,搜索“心梗”可匹配数据库中记录的“心肌梗死”
  • 法律咨询检索:法律术语有正式与通俗表达之分,用户即使不熟悉专业术语,也能使用通俗说法找到所需内容。例如,搜索“离婚”可匹配文书中的“解除婚姻关系”
  • 企业知识库问答:支持缩写、别称与正式名称并行使用,确保检索覆盖全面。例如,搜索“OA系统”可召回关于“办公自动化系统”的文档。

方案架构

图1 架构图
  • 索引阶段:原始数据经过同义词扩展后,所有同义词均被写入倒排索引。例如原文含“开心”,索引中同时存储“高兴”“快乐”“愉快”,无论用户使用哪个词搜索均可命中。
  • 查询阶段:用户输入的关键词同样经过同义词扩展,再与倒排索引匹配,实现双向语义覆盖。
  • 词库动态加载:同义词词库存储于OBS,更新后无需重启集群,新写入数据即可使用新词库。
  • 自定义分词器:由分析器standard/ik_smart/ik_max_word(基础分词)+ 同义词过滤器dynamic_synonym组合成自定义的分词器。

方案优势

  • 语义级搜索覆盖:突破字面匹配限制,实现“搜一个词、覆盖一组意思”的语义扩展搜索,大幅提升召回率。
  • 零代码配置:同义词规则以文本文件维护,业务运营人员即可自行添加新词组,无需修改任何代码或配置。
  • 动态更新:词库文件通过OBS动态加载,更新词库无需重启集群,线上业务不受影响。
  • 灵活的词组管理:一行定义一组同义词,支持任意数量词语组合,按业务需求灵活扩展。
  • 与主词词库协同:同义词词库与主词词库可同时配置使用,确保同义词组内的专有词语在分词阶段不被错误拆分。

约束限制

  • 集群版本要求:2018年3月10日之前创建的集群,不支持自定义词库功能。本文的示例代码基于OpenSearch集群和Elasticsearch 7.x版本的集群,Elasticsearch集群版本低于7.x的操作命令请参见示例代码(Elasticsearch版本<7.x)
  • 分词粒度影响:同义词扩展基于基础分词结果进行,如果某词在基础分词阶段已被拆分,则同义词扩展可能失效,建议将同义词组中的关键词同时加入主词词库,确保其在分词阶段被识别为独立词条后再进行同义词扩展。

前提条件

  • 目标集群处于“可用”状态,无正在进行中的任务。
  • 确认操作账号已具备以下权限(可以在IAM服务的“权限管理 > 权限”中查看和配置):
    • 配置自定义词库的权限:
      "css:IKThesaurus:*"
    • OBS桶及对象的读取权限:
      obs:bucket:getBucketLocation
      obs:bucket:getBucketStoragePolicy
      obs:bucket:listAllMyBuckets
      obs:object:getObject
    • 如果OBS桶使用SSE-KMS加密模式时,还需KMS权限:
      "kms:cmk:create",
      "kms:dek:create",
      "kms:cmk:get",
      "kms:dek:decrypt",
      "kms:cmk:list"

步骤一:准备词库文件

准备同义词词库,并上传至OBS桶中。

  1. 准备同义词词库文件“synonym_custom.txt”。要求编码均为UTF-8无BOM格式,每行一组同义词,词间以英文逗号“,”分隔,英文小写,且文件大小不超过100MB。
    开心,高兴,快乐,愉快
    手机,移动终端
  2. 将准备好的词库文件上传至OBS桶。

    OBS桶必须与CSS集群位于同一区域,且“存储类别”“标准存储”

步骤二:配置自定义词库

为集群配置自定义的同义词词库,使dynamic_synonym过滤器能够加载同义词规则。

  1. 登录云搜索服务管理控制台
  2. 在左侧导航栏,根据集群类型选择“集群管理 > Elasticsearch”“集群管理 > OpenSearch”
  3. 在集群列表,单击目标集群名称,进入集群详情页。
  4. 选择“集群配置 > 自定义词库”,进入自定义词库配置页面。
  5. 根据业务需求配置自定义词库。
    表1 配置自定义词库

    参数名称

    操作说明

    OBS桶

    选择词库文件所在的OBS桶。

    主词词库

    保持默认值“不更新”,表示不配置该词库。

    停词词库

    保持默认值“不更新”,表示不配置该词库。

    同义词词库

    选择“更新 > 选择”,选择OBS桶中已上传的同义词词库文件“synonym_custom.txt”,单击“确定”

  6. 配置完成后,单击“保存”,在弹窗中单击“确定”,启动词库更新。

    下方呈现词库配置信息。等待约1分钟,词库状态从“更新中”变更为“成功”,表示词库更新完成。

    图2 词库状态

步骤三:创建索引

创建索引并自定义分词器(组合分词和同义词扩展),使索引在写入和查询时都进行同义词扩展。

  1. 在自定义词库页面,单击页面右上角的“Kibana”“Dashboards”
  2. 在Kibana或Dashboards左侧导航栏选择“Dev Tools”,进入操作页面。
  3. 执行以下命令,创建索引book并自定义分词器ik_synonym(组合ik_smart分析器和dynamic_synonym过滤器)。
    PUT book
    {
      "settings": {
        "analysis": {
          "filter": {
            "my_synonym": {
              "type": "dynamic_synonym"
            }
          },
          "analyzer": {
            "ik_synonym": {
              "filter": [
                "my_synonym"
              ],
              "type": "custom",
              "tokenizer": "ik_smart"
            }
          }
        }
      },
      "mappings": {
        "properties": {
          "content": {
            "type": "text",
            "analyzer": "ik_synonym",
            "search_analyzer": "ik_synonym"
          }
        }
      }
    }
    参数说明:
    • my_synonym:自定义过滤器的名称。
    • ik_synonym: 自定义分词器的名称。
    • ik_smart:IK分词器提供的粗粒度分析器。
    预期结果:
    {
      "acknowledged" : true,
      "shards_acknowledged" : true,
      "index" : "book"
    }

步骤四:验证同义词分词效果

索引创建成功后,先验证同义词分词效果再导入数据,确保词库和分词器配置正确。

执行以下命令,查看自定义分词器ik_synonym的分词效果。
GET /book/_analyze
{
  "analyzer": "ik_synonym",
  "text": "我今天特别高兴"
}
预期结果:
{
  "tokens" : [
    { "token" : "我", "start_offset" : 0, "end_offset" : 1, "type" : "CN_WORD", "position" : 0 },
    { "token" : "今天", "start_offset" : 1, "end_offset" : 3, "type" : "CN_WORD", "position" : 1 },
    { "token" : "特别", "start_offset" : 3, "end_offset" : 5, "type" : "CN_WORD", "position" : 2 },
    { "token" : "高兴", "start_offset" : 5, "end_offset" : 7, "type" : "CN_WORD", "position" : 3 },
    { "token" : "开心", "start_offset" : 5, "end_offset" : 7, "type" : "SYNONYM", "position" : 3 },
    { "token" : "快乐", "start_offset" : 5, "end_offset" : 7, "type" : "SYNONYM", "position" : 3 },
    { "token" : "愉快", "start_offset" : 5, "end_offset" : 7, "type" : "SYNONYM", "position" : 3 }
  ]
}

结果解读:position 3位置同时出现“高兴”“开心”“快乐”“愉快”四个词,说明同义词扩展已生效。使用这四个词中任意一个搜索,均可命中该文档。

步骤五:写入测试数据

写入测试数据,用于验证同义词搜索效果。

执行以下命令,将测试文本写入索引book。

POST /book/_bulk
{"index":{"_id":"1"}}
{"content":"我今天特别高兴"}
{"index":{"_id":"2"}}
{"content":"孩子们玩得十分开心"}
{"index":{"_id":"3"}}
{"content":"你看他兴高采烈的"}

预期结果:

{
  "took" : 9,
  "errors" : false,
  "items" : [
    { "index" : { "_index" : "book", "_type" : "_doc", "_id" : "1", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1, "status" : 201 } },
    { "index" : { "_index" : "book", "_type" : "_doc", "_id" : "2", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 1, "_primary_term" : 1, "status" : 201 } },
    { "index" : { "_index" : "book", "_type" : "_doc", "_id" : "3", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 2, "failed" : 0 }, "_seq_no" : 2, "_primary_term" : 1, "status" : 201 } }
  ]
}

步骤六:执行同义词搜索

通过同义词搜索验证自定义词库的配置效果。

执行以下命令,搜索关键词“快乐”
GET /book/_search
{
  "query": {
    "match": {
      "content": "快乐"
    }
  }
}

预期结果:

{
  "took" : 15,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.8380518,
    "hits" : [
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.8380518,
        "_source" : {
          "content" : "我今天特别高兴"
        }
      },
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.8380518,
        "_source" : {
          "content" : "孩子们玩得十分开心"
        }
      }
    ]
  }
}

结果解读:文档1原文含“高兴”,文档2原文含“开心”,与搜索关键词“快乐”是同义词组,因此都被成功召回;文档3无相关词语,未被召回;说明同义词扩展生效。

示例代码(Elasticsearch版本<7.x)

当Elasticsearch集群版本低于7.x时,需要自定义type名称,参考以下命令。

  1. 执行以下命令,创建索引book并指定自定义分词器。
    PUT book
    {
      "settings": {
        "analysis": {
          "filter": {
            "my_synonym": {
              "type": "dynamic_synonym"
            }
          },
          "analyzer": {
            "ik_synonym": {
              "filter": [
                "my_synonym"
              ],
              "type": "custom",
              "tokenizer": "ik_smart"
            }
          }
        }
      },
      "mappings": {
        "type1": {
          "properties": {
            "content": {
              "type": "text",
              "analyzer": "ik_synonym",
              "search_analyzer": "ik_synonym"
            }
          }
        }
      }
    }
  2. 执行以下命令,将测试数据写入索引book。
    POST /_bulk
    { "index" : { "_index" : "book", "_type" : "type1", "_id" : "1" } }
    { "content" : "我今天特别高兴" }
    { "index" : { "_index" : "book", "_type" : "type1", "_id" : "2" } }
    { "content" : "孩子们玩得十分开心" }
    { "index" : { "_index" : "book", "_type" : "type1", "_id" : "3" } }
    { "content" : "你看他兴高采烈的" }
  3. 执行以下命令,通过同义词搜索验证配置结果。
    GET /book/type1/_search
    {
      "query": {
        "match": {
          "content": "快乐"
        }
      }
    }

相关文档