配置简繁体转换搜索
在跨地区业务场景中,用户可能会遇到使用简体中文搜索繁体中文数据,或以繁体中文搜索简体中文数据的情况,例如,输入“国际”希望召回包含“國際”的文档。然而,由于简体和繁体之间的字形差异,直接搜索会无法准确召回目标文档,导致用户体验不佳。为此,CSS服务为Elasticsearch集群预置了简繁分析插件(analysis-stconvert),通过在分词分析阶段自动完成简繁体的转换,用户无需修改业务数据或编写转换逻辑,只需在创建索引时配置对应的分析器,即可实现“输入简体命中繁体”或“输入繁体命中简体”的跨字形语义搜索。
功能介绍
简繁分析插件(analysis-stconvert)是一款支持中文简体与繁体相互转换的插件,CSS服务默认已安装,开箱即用。其核心能力是在文本分析阶段对字符进行转换,使简体与繁体在索引层面统一,从而实现跨字形的搜索匹配。
| 转换类型 | 说明 | 示例 |
|---|---|---|
| s2t | 简体转繁体。 | “手机” > “手機” |
| t2s | 繁体转简体。常用类型,IK分词器的词库以简体中文为主,对简体文本的分词效果优于繁体。 | “國際” > “国际” |
简繁分析插件可灵活嵌入分词器的不同处理阶段,支持以下四种使用方式:
| 使用方式 | 说明 | 适用场景 |
|---|---|---|
| char_filter(字符过滤器) | 在分词之前对原始字符串进行转换。
| 需要先统一字形、再分词(依赖分词器)。推荐方式,本文采用此方案。 |
| tokenizer(分词器) | 将转换与分词合并在同一步骤完成。 | 轻量场景,不需要叠加其他分词器。 |
| token_filter(词元过滤器) | 在分词之后对token进行转换。 | 需要先分词(依赖分词器)、再对词语进行字形统一。 |
| analyzer(分析器) | 将插件直接作为完整的分析器使用。 | 仅需简繁转换,无需IK等中文分词能力。 |
简繁转换的工作原理
本文采用char_filter(tsconvert)方式,将简繁转换嵌入IK分词器之前,实现字形统一。
关键流程说明:
- 索引阶段:原始数据先经char_filter(tsconvert)将繁体统一为简体,再经IK分词器(ik_smart)分词,分词结果写入倒排索引。例如原文“國際電視臺”被转换为“国际电视台”后再分词存储。
- 查询阶段:搜索关键词同样先统一为简体再分词,与倒排索引匹配,实现简繁互搜。
- 自定义分析器:由ik_smart粗粒度分析器与tsconvert字符过滤器组合而成,写入与查询均使用该分析器。
前提条件
- 目标集群处于“可用”状态,无正在进行中的任务。
- 如果自定义的简繁转换分析器依赖自定义词库,则需提前完成自定义词库配置,操作步骤请参见配置自定义词库。
登录Kibana
登录Kibana进入命令执行页面。Elasticsearch集群支持多种客户端访问,本文仅以CSS服务集成的Kibana为例介绍配置指导。
- 登录云搜索服务管理控制台。
- 在左侧导航栏,选择“集群管理 > Elasticsearch”。
- 在集群列表,选择目标集群,单击操作列的“Kibana”,登录Kibana。
- 在Kibana左侧导航栏选择“Dev Tools”,进入操作页面。
控制台左侧是命令输入框,其右侧的三角形图标为执行按钮,右侧区域则显示执行结果。
创建索引
创建索引并定义包含简繁转换逻辑的自定义分析器,使索引在写入和查询时都先统一字形再分词。
执行以下命令,创建索引test并自定义分析器ts_ik(组合ik_smart和tsconvert)。
PUT /test
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"analysis": {
"analyzer": {
"ts_ik": {
"tokenizer": "ik_smart",
"char_filter": [
"tsconvert"
]
}
},
"char_filter": {
"tsconvert": {
"type": "stconvert",
"convert_type": "t2s"
}
}
}
},
"mappings": {
"properties": {
"desc": {
"type": "text",
"analyzer": "ts_ik"
}
}
}
} | 参数 | 说明 |
|---|---|
| ts_ik | 自定义分析器,先经tsconvert字符过滤器将繁体统一为简体,再由ik_smart完成分词。 |
| char_filter.tsconvert | 自定义字符过滤器,类型为stconvert(简繁转换插件),convert_type: t2s表示繁体转简体。 |
| convert_type: t2s | 繁体转简体,用于将繁体原文统一为简体,便于后续分词。IK分词器对简体中文分词更准确,因此推荐统一转为简体。 |
| analyzer: ts_ik | 将desc字段绑定到自定义分析器,写入与查询均使用该分析器处理。 |
预期结果:
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "test"
} 验证分词效果
索引创建成功后,先验证自定义分析器效果再导入数据,确保简繁转换与分词配置正确。
执行以下命令,查看自定义分析器ts_ik对繁体文本的转换与分词结果。
POST /test/_analyze
{
"analyzer": "ts_ik",
"text": "國際電視臺"
} 预期结果:
{
"tokens" : [
{ "token" : "国际", "start_offset" : 0, "end_offset" : 2, "type" : "CN_WORD", "position" : 0 },
{ "token" : "电视台", "start_offset" : 2, "end_offset" : 5, "type" : "CN_WORD", "position" : 1 }
]
} 结果解读:繁体“國際電視臺”被转换为简体“国际电视台”后,ik_smart将其切分为“国际”和“电视台”,转换与分词均正常。
导入数据
向索引中写入繁体或简体内容,验证简繁混合数据的搜索效果。
执行以下命令,将测试数据写入索引test。
POST /test/_bulk
{"index":{"_id":"1"}}
{"desc":"國際電視臺"}
{"index":{"_id":"2"}}
{"desc":"国际大城市"}
{"index":{"_id":"3"}}
{"desc":"联合国"} 预期结果:
{
"took" : 6,
"errors" : false,
"items" : [
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "1", "_version" : 1, "result" : "created", "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1, "status" : 201 } },
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "2", "_version" : 1, "result" : "created", "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "_seq_no" : 1, "_primary_term" : 1, "status" : 201 } },
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "3", "_version" : 1, "result" : "created", "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "_seq_no" : 2, "_primary_term" : 1, "status" : 201 } }
]
} 验证简繁转换搜索
通过简体和繁体关键词分别搜索,验证跨字形搜索的效果。
GET /test/_search
{
"query": {
"match": {
"desc": "国际"
}
}
} GET /test/_search
{
"query": {
"match": {
"desc": "國際"
}
}
} 两次搜索的预期结果一致:
{
...
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 0.43445712,
"hits" : [
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.43445712,
"_source" : {
"desc" : "國際電視臺"
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.43445712,
"_source" : {
"desc" : "国际大城市"
}
}
]
}
} 结果解读:无论用简体“国际”还是繁体“國際”搜索,均能同时召回繁体文档(文档1)和简体文档(文档2),且得分一致,说明简繁转换双向生效。文档3“联合国”不包含“国际”,未被召回。
常见问题:
- 问题1:如何配置“简体搜索命中繁体文档”?
创建索引时,将tsconvert(t2s)这个char_filter替换为stconvert(s2t)即可。索引文档时简体被转为繁体存储,搜索时同样转为繁体匹配。配置如下:
PUT /test1 { "settings": { "number_of_shards": 1, "number_of_replicas": 0, "analysis": { "analyzer": { "ts_ik": { "tokenizer": "ik_smart", "char_filter": [ "stconvert" ] } }, "char_filter": { "stconvert": { "type": "stconvert", "convert_type": "s2t" } } } }, "mappings": { "properties": { "desc": { "type": "text", "analyzer": "ts_ik" } } } } - 问题2:已有索引的文档想补充简繁转换支持,需要重建索引吗?
分析器配置只能在索引创建时设定,已存在的索引无法修改分析器。是否需要重建索引取决于现有数据的情况:
- 现有数据全部为简体,新增繁转简分析器后,不需要重建索引,因为数据本身已是简体,新增分析器后新写入的繁体数据会自动转换,历史数据无需处理。
- 现有数据包含繁体数据,新增繁转简分析器后,需要重建索引,因为历史繁体数据未经转换,无法被简体关键词命中。
重建索引的两种方式:- 方式一:原地更新(适用于数据量较小的场景)
POST /{my_index}/_update_by_query该命令会对已有文档重新执行分析器处理,但不会改变索引的分析器配置。如果索引创建时未配置简繁转换分析器,此方式无法生效。
- 方式二:通过索引别名(Alias)无缝切换
- 新建配置了简繁转换分析器的索引。
- 通过_reindex将旧索引数据迁移至新索引。
POST /_reindex { "source": { "index": "old_index" }, "dest": { "index": "new_index" } } - 将别名指向新索引。
POST /_aliases { "actions": [ { "remove": { "index": "old_index", "alias": "my_alias" } }, { "add": { "index": "new_index", "alias": "my_alias" } } ] } - (可选)删除旧索引,释放存储空间。
DELETE /old_index
示例代码(Elasticsearch版本<7.x)
当Elasticsearch集群版本低于7.x时,需要自定义type名称,参考以下命令。
- 执行以下命令,创建索引test并指定自定义分析器。
PUT /test { "settings": { "number_of_shards": 1, "number_of_replicas": 0, "analysis": { "analyzer": { "ts_ik": { "tokenizer": "ik_smart", "char_filter": [ "tsconvert" ] } }, "char_filter": { "tsconvert": { "type": "stconvert", "convert_type": "t2s" } } } }, "mappings": { "type1": { "properties": { "desc": { "type": "text", "analyzer": "ts_ik" } } } } } - 执行以下命令,将测试数据写入索引test。
POST /_bulk { "index" : { "_index" : "test", "_type" : "type1", "_id" : "1" } } { "desc" : "國際電視臺" } { "index" : { "_index" : "test", "_type" : "type1", "_id" : "2" } } { "desc" : "国际大城市" } { "index" : { "_index" : "test", "_type" : "type1", "_id" : "3" } } { "desc" : "联合国" } - 执行以下命令,通过关键词搜索验证配置结果。
GET /test/type1/_search { "query": { "match": { "desc": "國際" } } }
