十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TongSearch简繁转换插件实战:让搜索同时命中简繁内容

TongSearch简繁转换插件实战:让搜索同时命中简繁内容 1. 搜索“幹細胞”查不到“干细胞”简繁体转换到底解决了什么问题1.1 一个真实得不能再真实的搜索事故先说个我亲身遇到的场景。之前帮一家做医疗健康科普站点的团队排查搜索问题他们的内容库里既有来自繁体中文源站的文章也有运营手工录入的简体文章结果用户搜索“幹細胞”时简体内容一条都搜不出来。用户那边一脸懵运营那边更懵——明明数据库里有大量干细胞相关的文章怎么就搜不到呢原因说穿了不值钱简体“干”和繁体“幹”在字符层面是两个完全不同的码点搜索引擎默认按精确字符匹配并不会帮你做语言学上的等价替换。类似的问题还有“白血病”和“白血病”“血”字在繁体环境常写作“血”本身同形但更多字完全不同、“打印机”和“印表機”这是两岸用词差异比简繁转换更深一层等等。这个问题的本质是搜索系统的相关性判断建立在分词和字符归一化之上而简繁差异属于字符归一化没有做完整的典型场景。TongSearch 里提供了一款叫analysis-stconvert的转换插件专门解决这个问题。这篇文章就从安装、配置、原理、踩坑四个维度把它讲透适合正在用 TongSearch 做站内搜索、电商搜索或者内容检索的同学参考。1.2 简繁体转换在搜索链路中的位置要理解这个插件先得知道它工作在哪个环节。TongSearch 的索引和查询链路大体是原始文本 →分析器Analyzer→ 分词器Tokenizer → Token 过滤器TokenFilter → 倒排索引 / 查询词项。简繁体转换属于典型的 TokenFilter 职责或者在某些实现里也可以在分析器的最前面做一次字符映射。analysis-stconvert做的事情就是在分词前或分词后把文本里的繁体字映射成简体字或者反过来让索引侧和查询侧落到同一个字符上。这里有个关键点转换必须同时作用于索引端和查询端。否则就会出现“索引全简、查询繁体”或者“索引繁体、查询简体”的错位转换做了等于白做。后面配置部分我会专门强调这一点这也是新手最容易漏的地方。1.3 为什么不在业务代码里做转换非要靠插件有人可能会问既然只是字符映射我在后端用 OpenCC 把数据统一转成简体再入库查询时也统一转简体不就行了吗为什么还要引入插件这个想法本身没错小规模项目完全可行。但一旦业务复杂起来代码里做转换会有几个麻烦转换逻辑散落多处入库要转、查询要转、搜索建议要转、高亮片段要转每个地方都要维护一套调用漏一处就出 bug。规则难统一业务代码里的转换规则和词典往往是硬编码的换词库、加自定义词条要发版。对查询无法感知上下文比如有的场景是“索引简体、查询允许繁体”有的场景是“索引繁体、查询只收简体”这种按索引和查询分别控制的灵活性代码里做会比较别扭。把这些逻辑下沉到搜索引擎的分析器层面用配置驱动索引和查询两侧通过不同的 Analyzer 组合来控制转换方向就清爽得多。这也是analysis-stconvert这类插件存在的核心价值——用配置项替代业务代码把字符归一化变成搜索基础能力的一部分。2. 插件的安装与基础配置把转换器跑起来2.1 TongSearch 插件机制与安装路径TongSearch 的插件体系比较接近主流搜索引擎的形态插件通常是一个包含配置文件和实现类的目录放到指定目录后重启节点即可加载。analysis-stconvert的安装步骤大致如下从 TongSearch 对应的版本仓库下载匹配版本的插件包。版本号一定要和 TongSearch 主版本严格对应差一个小版本都可能出现类加载异常。解压到 TongSearch 的plugins/analysis-stconvert目录下。确认插件目录下有plugin-descriptor.properties之类的元信息文件以及核心 jar 包。重启节点查看启动日志中是否出现类似loaded plugin [analysis-stconvert]的字样。安装本身不复杂但有两个细节值得注意。第一不要直接拷贝生产环境正在用的插件目录到测试环境插件包里的某些本地化配置比如自定义词典路径可能是绝对路径拷过去容易踩坑。第二安装插件后一定要验证配置能被正确加载光看启动日志还不够最好用后面会提到的_analyze接口实际跑一段文本确认效果。2.2 配置一个“简繁通吃”的分析器安装完成之后核心工作就是配置分析器。一个最简单、可用的分析器配置长这样{ settings: { analysis: { analyzer: { stconvert_analyzer: { type: custom, tokenizer: standard, filter: [stconvert_filter] } }, filter: { stconvert_filter: { type: stconvert, convert_type: t2s, keep_origin: false } } } } }这个配置里有两个参数需要重点解释convert_type转换方向。t2s表示繁体转简体s2t表示简体转繁体。这是最常用的两个值。keep_origin是否保留原始字符。设置成true时会把转换前和转换后的内容都保留下来适合那些希望“简体和繁体都能从索引里命中”的场景。实际项目中我更推荐keep_origin设为true因为用户可能两种写法都会搜保留原始形式相当于扩展了召回只是索引体积会稍大一些。2.3 分词链路的顺序转换、分词、过滤谁先谁后分析器内部是有执行顺序的先 Tokenizer 后 TokenFilter。stconvert既可以配置成 TokenFilter也可以在加载词典阶段对文本整体做归一化。从我实际使用的经验看把转换放在分词之后、其他过滤之前是个比较稳的顺序。举个例子。假设原始文本是“繁體字測試”分词器把它切成“繁”、“體”、“字”、“測”、“試”然后stconvert把每个 token 转成“繁”、“体”、“字”、“测”、“试”。这种逐 token 转换的好处是转换结果的粒度受分词控制后续其他过滤器比如小写转换、停用词过滤可以基于转换后的词继续处理整个链路清晰可控。如果放在分词之前做整体转换逻辑上也没错但有些分词器对繁体文本的切分效果可能不如简体文本先转换可以让分词器面对更“标准”的输入。两种方案没有绝对优劣关键是要保证索引端和查询端用的是同一条链路。最忌讳的是索引用“先分词再转换”查询用“先转换再分词”两边分析结果不一致相关性必然出问题。3. 转换规则与词典机制stconvert 不是简单字典替换3.1 转换方向t2s、s2t 与区域变体很多人以为简繁转换就是“一对一”的字形替换实际上没那么简单。一个字可能对应多个繁体字。最经典的例子就是“发”对应“發”发射、发展和“髮”头发“面”对应“面”脸面和“麵”面条。这种一对多的关系靠单一字符映射表搞不定必须结合词汇上下文来判断。analysis-stconvert内置的词典包含大量词级别的转换条目比如“发展”转成“發展”“头发”转成“頭髮”通过最长匹配来处理这种歧义。另外繁体中文本身还有区域差异。台湾地区习惯用“程式”香港地区习惯用“程序”用词不同字符也不同。convert_type除了t2s和s2t一些版本的插件还支持带区域变体的转换比如s2tw简体转台湾繁体、s2hk简体转香港繁体。如果你的用户群体集中在特定区域可以按需调整转换方向。3.2 插件内置词典与自定义词条内置词典是插件开箱即用的基础但任何词典都不可能覆盖所有领域词汇尤其是专业术语、人名地名、品牌名。这时候就需要自定义词条。自定义词典的管理方式在不同版本里略有差异常见做法是在配置里指定一个词库文件路径{ filter: { stconvert_filter: { type: stconvert, convert_type: t2s, custom_word_path: analysis/stconvert/custom.txt } } }词库文件的格式一般是每行一个词条用分隔符把源词和目标词分开比如幹細胞,干细胞 網路,网络 軟體,软件加词库之后和内置词典一样参与匹配。有几个实践心得分享给各位不要一开始就追求大而全的词库先跑一段时间从搜索日志里把用户搜了但召回为 0 的词捞出来逐个补充效率最高。自定义词库要按业务域维护医疗、法律、电商各自的术语差异很大混在一张表里匹配顺序容易打架。改词库后要重建索引因为新增转换词条会影响索引侧的分词结果只改配置不重建新旧数据的行为会不一致。3.3 从源码看转换流程简单线性扫描匹配如果你研究过插件源码会发现转换流程本身并不神秘大致是一个“词典加载 线性扫描 最长匹配”的过程启动时把内置词典和自定义词典加载进内存通常组织成某种树形结构方便前缀匹配。收到待转换文本后从第一个字符开始尝试匹配词典里以该字符开头的词条。匹配到多个词条时取最长的那条作为转换结果如果没有匹配到任何词条原样输出当前字符。移动扫描指针继续处理下一个字符。这个流程决定了插件的一个天然特性它的转换准确度上限取决于词典覆盖率。词典里有的词转换得准词典里没有的词就只能靠单字映射兜底单字映射也没有的就原样保留。所以要提高转换质量重点不是改插件代码而是持续完善自定义词典。4. 实战让搜索“干细胞”能命中“幹細胞”数据4.1 索引 mapping 配置理解原理之后实战就顺理成章了。我先给出一套完整的索引配置再逐项解释。假设我们要给一个医学内容库建索引字段有title和content{ settings: { analysis: { analyzer: { zh_same_analyzer: { type: custom, tokenizer: standard, filter: [ stconvert_filter, lowercase ] } }, filter: { stconvert_filter: { type: stconvert, convert_type: t2s, keep_origin: true } } } }, mappings: { properties: { title: { type: text, analyzer: zh_same_analyzer, search_analyzer: zh_same_analyzer }, content: { type: text, analyzer: zh_same_analyzer, search_analyzer: zh_same_analyzer } } } }这个配置的关键决策有两点。第一索引端和查询端用同一个zh_same_analyzer保证两侧转换逻辑完全一致。这是因为keep_origin已经开启了索引里既存了“幹細胞”的原始 token也存了“干细胞”的转换后 token无论用户用哪种写法查都能命中同一批文档。第二lowercase过滤器放在转换之后处理英文和数字的大小写问题避免“CT”和“ct”不一致又引入新的召回问题。4.2 查询端配置用上面的配置建好索引后查询侧就不用额外配置了直接用match查询即可{ query: { match: { title: { query: 幹細胞 } } } }查询词“幹細胞”经过同一个分析器被转成“干细胞”而索引里的文档同样可能存了“干细胞”和“幹細胞”两个 token于是精确匹配成功。这里有个容易踩的误区查询端不要自己再做一次字符串替换然后把替换后的词传给搜索。比如在代码里判断查询词包含繁体就转成简体再搜这样会导致查询分析器拿到的已经是“干细胞”convert_type的配置反而派不上用场万一某个词转换错了你在业务代码里还不好排查。插件能做的事就交给插件做。4.3 验证转换效果配置完成之后务必用分析接口验证两件事一是验证索引分析器的输出二是验证查询分析器的输出。{ analyzer: zh_same_analyzer, text: 研究幹細胞的發展 }预期结果是 tokens 里既包含类似于“幹”、“細胞”、“發展”这类原始形式也包含“干”、“细胞”、“发展”这类转换后的形式。如果你的版本里stconvert是逐 token 处理的输出可能更细粒度但核心判断标准是一样的能同时看到转换前和转换后的 token配置就算生效了。验证完分析器再插入几条测试数据分别用简体词和繁体词搜索对比召回结果是否一致。这一步别偷懒很多环境差异词典没加载、节点没重启、配置没同步都能通过这个测试暴露出来。5. 这里有一些坑建议先收藏5.1 同名同形异体字与多音字简繁转换最大的坑是那些“同一个字符、多种转换结果”的情况。前面提到的“发/發/髮”已经够让人头疼了还有一类更隐蔽异体字和台湾/香港的用字习惯差异。比如“裏”和“裡”在繁体世界都常见转简体都是“里”这没问题但反过来简体“里”转繁体时到底转成“裡”还是“裏”如果内置词典偏向台湾用法会选“裡”如果偏向传统字形会选“裏”。这种差异不影响简体用户搜索但在繁体内容生产和检索时会对不上。这类问题没有一劳永逸的解法。我的建议是先明确你的目标用户群体使用哪种繁体变体再选择对应区域的词库配置不要幻想一个配置满足所有区域做不到的。5.2 转换后关键词不可逆有的业务场景需要根据转换后的词回显原文这时候要注意stconvert不是可逆映射。“幹細胞”转简体是“干细胞”但“干”这个简体字有可能来自繁体“幹”干杯也有可能来自“乾”干燥还有可能来源就是“干”干涉。如果你把转换后的文字直接存进高亮字段展示给用户看的原文会变成“简体词”繁体原文丢了。针对这个场景比较稳的做法是保留原始字段的副本比如在 mapping 里同时存一个title_original字段不经过转换分析器专门用于结果展示。索引体积会变大但高亮准确度和用户展示体验的提升是值得的。5.3 性能开销与缓存转换过程涉及词典查找和字符串操作对查询性能有一定影响。词典越大加载时间和单次转换耗时会略有上升但对绝大多数业务场景来说这个开销还在可接受范围内。真正需要注意的是索引侧。如果你对content这种大字段开启了转换keep_origin又是true索引体积会有明显增长因为每个 token 都相当于存了两份。我见过一个项目开启简繁转换后索引体积增长了约 30%这个比例对磁盘和内存都有压力上线前要做好容量评估。另外如果查询频率很高建议在应用层加一层查询词转换缓存。毕竟同一个查询词在一段时间内会被大量重复搜索缓存转换结果能省去重复计算的开销。5.4 脏数据导致转换失败最后说一个很多人忽略的问题数据里的繁体字可能根本不是标准繁体。比如用户从 PDF 复制出来的文字、OCR 识别结果、手写输入法的输出都可能带有大量非标准字形、异体字、甚至乱码。这类字符在词典里匹配不上就会原样保留搜索结果自然还是差。遇到这种情况可以先做一轮数据清洗用统一码规范化如 NFC/NFKC处理字符把兼容字符转成标准形式再交给转换插件。这一步虽然简单但往往能解决很多搜索率上不去的“玄学”问题。6. 进阶转换插件与分词、拼音插件的配合思路6.1 和 ik 分词器搭配standard分词器对中文的支持比较弱实际项目中通常会换成ik_max_word之类的分词器。和analysis-stconvert搭配时分词器的选择会影响转换效果。我的推荐配置是先用 ik 分词再对每个词做简繁转换。因为 ik 是基于词典的分词器对简体文本的切分通常更稳定反过来如果先转换繁体到简体再交给 ik 分词同样没问题。但要注意的是ik 的词典里如果存的是简体词它面对繁体文本时的分词效果会打折扣所以更建议先转换后分词。不过如果你在索引端使用了keep_origin: true原始繁体文本也会被保留这等于给分词器提供了完整的原文上下文分词质量会更好。基于这个考虑把stconvert配置在 ik 分词之后既保留了原文语义环境又额外生成了简体 token是我比较常用的组合。6.2 和拼音插件搭配实现繁简拼音混合搜索再进一步简繁转换和拼音转换可以叠加实现“用户输入拼音、甚至输入繁体中文字符串都能命中简体内容”的效果。链路大概是原始文本 → 简繁转换 → 拼音转换 → 索引。这样“幹細胞”转成“干细胞”再转成拼音“ganxibao”用户搜索“ganxibao”也能命中。反过来用户搜索“幹細胞”经过简繁转换后走同一套拼音流程同样能命中。这个链路对电商、知识库、医疗搜索特别有用因为很多用户习惯输入拼音或者使用的是繁体输入法。但叠加使用时要注意转换链越长单个 token 的变形越多召回提升了精确率可能会下降。建议做成可选索引字段比如正常字段走简繁转换额外加一个拼音字段用于兜底召回而不是直接替换掉原字段的分析链路。6.3 未来扩展从简繁转换到更通用的术语归一化用熟analysis-stconvert之后你会发现这套思路可以延伸到更广的“搜索归一化”问题上。比如“打印机”和“印表機”这不仅仅是字符简繁的差异而是两岸用词差异。再比如“硬盤”和“硬盘”的差异算简繁但“U盘”和“隨身碟”就完全是词汇层面的差异了。处理这类问题思路和stconvert完全一致在分析阶段做词级别的替换映射把不同写法归一化到同一个 token 上。TongSearch 生态里这类归一化能力往往还是通过同一个插件机制扩展实现的。也就是说你学会配置analysis-stconvert之后再学其他自定义分析插件的成本会低很多因为它们遵循同一套分析器、过滤器、配置项的体系。6.4 一个真实项目的整体配置示例最后给出一份我在实际项目中用过的、比较完整的配置组合供参考。场景是面向两岸三地用户的医疗内容搜索索引字段同时支持简繁输入和拼音输入。{ settings: { analysis: { analyzer: { zh_search_analyzer: { type: custom, tokenizer: ik_max_word, filter: [ stconvert_filter, pinyin_filter, lowercase ] } }, filter: { stconvert_filter: { type: stconvert, convert_type: t2s, keep_origin: true }, pinyin_filter: { type: pinyin, keep_full_pinyin: true, keep_joined_full_pinyin: true, keep_original: true, lowercase: true } } } }, mappings: { properties: { title: { type: text, analyzer: zh_search_analyzer, search_analyzer: zh_search_analyzer }, content: { type: text, analyzer: zh_search_analyzer, search_analyzer: zh_search_analyzer } } } }强调一下这份配置适合召回优先的场景。如果你的业务对精确率要求很高可以把keep_origin改回false或者把拼音过滤单独挪到一个pinyin_field子字段里避免拼音扩展污染主字段的排序。配置没有绝对的最优解只有最适合你业务场景的组合。简繁转换插件的价值不在于“转换”本身而在于它把一种常见的字符归一化需求变成了搜索基础能力的一部分让索引、查询、结果展示可以围绕统一规则来协作。我自己在配置的时候最深的体会是任何分析层的能力都要索引端和查询端保持一致并且用真实数据反复验证这个基础打牢后面的搜索效果才不会出大问题。
返回列表