搜索全站
插件 11 分钟阅读

SearchWP 与 Relevanssi 站内搜索增强:中文分词与权重配置现实问题

WordPress 默认搜索又慢又不准。SearchWP 与 Relevanssi 都能自建索引替代 LIKE 查询,但中文分词是绕不开的坎。本文对比两者机制、权重配置方法与中文场景的替代方案。

U
UFO_PF 2024-10-21 · 阅读 11 分钟
Se 插件
阅读提示
本文约 4,201 字,预计阅读 11 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:搜”免备案”搜不出写”免备案”的文章

在 WordPress 上做中文站点,搜索功能几乎必然让人失望:搜”虚拟主机”,搜不出标题里写”虚拟主机”的文章(因为作者写的是”虚机”);搜”免备案”,返回一堆只含”备案”两个字的文章;文章数过万之后,一次搜索要花两三秒,前台转圈。

根因是 WordPress 默认搜索走的是 LIKE '%关键词%'(本站另有专文拆解原理),既不分词也不排序,中文里”免备案”和”备案”在它眼里是两个毫不相干的字符串。

站内搜索增强插件做的事就是”自己建一套索引”:把每篇文章的标题、正文、摘要、分类名、自定义字段拆成词条,存进索引表,搜索时按词频与权重算分排序。SearchWP 和 Relevanssi 是这个领域最主流的两个选择,前者是商业插件,后者有免费版与付费版(Premium)。

原理分析:两套索引机制

维度 Relevanssi SearchWP
授权 免费版 + Premium 付费 纯付费(按站点数授权)
索引存储 自建 wp_relevanssi 表 自建索引表(非 postmeta)
是否依赖 MySQL FULLTEXT 不依赖 不依赖
权重配置 后台 Weights(标题/内容/分类/评论/自定义字段) Search Engines 的 Sources 与属性权重滑块
模糊匹配 有 Fuzzy 开关(部分匹配) 有 Partial matches / Stemming
索引中文 需额外处理 需额外处理
特色 同义词、Did you mean(Premium) Live Search、Metrics、PDF/文档内容索引(扩展)

两者的共同点是不再依赖 MySQL 的全文索引,所以绕开了 InnoDB 全文索引对中文不友好这一层限制。但绕开之后还有第二层问题:插件自己也要分词,而它们的分词器默认都是按”空白字符 + 标点”切的。中文没有空格,一句”如何配置 Nginx 缓存”在默认情况下可能被切成两个 token:如何配置 和 Nginx 和 缓存(按标点/空格切)。如果用户搜”配置缓存”,就可能匹配不上。

这一点必须提前认清:装了搜索插件不等于中文搜索就好了。实测下来,中文场景下常见的三种可行做法是:

  1. 接受按标点粗切,靠”模糊匹配 + 权重”弥补(适合文章量小、词汇集中的站点);
  2. 在索引阶段用钩子对中文做二元切分(bigram),牺牲索引体积换召回率;
  3. 上外部搜索引擎(Elasticsearch + IK 分词、Meilisearch),把搜索彻底外包(成本最高,效果最好)。

保姆级实操

第一步:安装并重建索引

Relevanssi 安装后必须先建索引,否则搜索没有变化:

  1. 后台「Relevanssi → Indexing」;
  2. Indexing options 里勾选要索引的内容:Posts、Pages、自定义文章类型、Comments、Custom fields(填字段名或选 all)、Taxonomies;
  3. 点「Build the index」(或「Index all posts」),等待完成;
  4. 文章量大时,索引构建可能超时,分批执行并在后台确认剩余数量。

之后新增/修改文章会自动增量索引,但批量导入数据后要手动重建一次。

第二步:配置权重与匹配方式

「Relevanssi → Searching」里的关键项:

  • Default operator:AND(要求所有词都命中,结果更精准)或 OR(召回更多);
  • Fuzzy matching:允许部分匹配,中文场景下能显著提升召回,但也会带来噪声;
  • 「Relevanssi → Weights」里调 Title weight(标题权重)、Content weight、Comment weight、Taxonomy weight。中文站点通常把标题权重调到 3–5 倍;
  • Minimum word length:默认会忽略过短的词,中文单字/双字词很多,这一项要按实际情况放低,否则”云主机”这类两字词可能被忽略。

SearchWP 的对应配置在「Settings → SearchWP → Search Engines」:为每个引擎指定 Sources(文章类型、分类法、自定义字段),再用滑块调各属性的权重(Title、Content、Excerpt、Slug、Taxonomy、Comments),开启 Partial matches 与 Stemming。

第三步:用钩子干预结果排序

想让”产品”优先于”文章”出现在搜索结果里:

add_filter( 'relevanssi_hits_filter', function ( $hits ) {
    if ( empty( $hits[0] ) ) {
        return $hits;
    }
    usort( $hits[0], function ( $a, $b ) {
        $rank_a = 'product' === $a->post_type ? 0 : 1;
        $rank_b = 'product' === $b->post_type ? 0 : 1;
        if ( $rank_a === $rank_b ) {
            return 0;
        }
        return $rank_a < $rank_b ? -1 : 1;
    } );
    return $hits;
} );

把某些内容排除出索引(如落地页、隐私政策):

add_filter( 'relevanssi_do_not_index', function ( $do_not_index, $post ) {
    if ( in_array( $post->post_type, array( 'landing_page', 'attachment' ), true ) ) {
        return true;
    }
    if ( has_term( 'internal', 'post_tag', $post ) ) {
        return true;
    }
    return $do_not_index;
}, 10, 2 );

第四步:验证索引是否真的覆盖了你关心的内容

# 前台搜索并观察耗时:先在后台关掉页面缓存,避免读到缓存结果
curl -s -o /dev/null -w "耗时 %{time_total}s\n" "https://mimangfei.com/?s=nginx%20%E7%BC%93%E5%AD%98"

同时确认自定义字段被索引:搜一个只出现在 ACF 字段里的词,能搜到才算生效。ACF 字段的索引前提是该字段已按 ACF 字段组与前台调用 的方式正确存储并被插件选中索引。

第五步:中文分词的落地取舍

如果插件的粗切分不能满足需求,按数据量分档处理:

  • 1 万篇以内:开启模糊匹配 + 提高标题权重,通常够用;把”同义词”手工维护起来(同义词功能在 Relevanssi Premium 里提供,SearchWP 也有类似能力);
  • 1 万–10 万篇:考虑在索引阶段对中文做二元切分,索引体积会增大 2–3 倍,但召回率明显提升;
  • 10 万篇以上 / 电商场景:上 Elasticsearch(配 IK 分词器)或 Meilisearch,由专门的搜索引擎承担,WordPress 只负责发查询请求。

常见踩坑

  1. 建完索引没效果。 多半是主题自带搜索模板直接写了自己的 WP_Query,绕过了主查询。检查模板里有没有 new WP_Query( array( 's' => ... ) ),或是否用了 pre_get_posts 覆盖。
  2. 自定义字段没被索引。 默认只索引正文,需要在插件设置里显式勾选字段名。
  3. Minimum word length 把中文短词过滤掉了。 中文常用词多为两字,阈值设成 3 会直接废掉大批关键词。
  4. 开启模糊匹配后噪声剧增。 搜”缓存”出来一堆”缓存里”以外的词,需要在权重里把标题命中调高,压低正文噪声。
  5. 索引表膨胀拖慢备份。 索引表体积常常是 wp_posts 的 1–2 倍,备份时要么跳过索引表(恢复后重建索引),要么接受更大的备份文件。
  6. 中文搜”微信小程序”搜不到写”WeChat 小程序”的文章。 这是词表问题,靠同义词维护解决,插件不会自动知道。
  7. 案例: 某文档站 6000 篇文章,装上搜索插件后搜”伪静态”依然搜不出标题为”WordPress 伪静态设置”的文章。排查发现插件的 tokenizer 把整句中文当成一个 token,只有完整匹配整句才能命中。最后采取的折中方案是:在索引阶段对中文按二元切分(”伪静态”切成”伪静””静态”),索引体积从 90MB 涨到 260MB,但搜索召回率和准确率都大幅提升。教训:中文搜索的关键是分词,不是插件品牌;先想清楚分词策略,再决定用哪个插件,否则换插件解决不了问题。

总结

选型的三个判断点:预算(SearchWP 纯付费,Relevanssi 有免费版)、是否需要专业特性(SearchWP 的 Live Search、Metrics、PDF 索引;Relevanssi Premium 的同义词与 Did you mean)、以及最重要的中文分词策略(必须提前设计)。

落地顺序:先建索引并验证自定义字段被覆盖 → 调标题/正文权重与匹配方式 → 用 relevanssi_hits_filter 之类钩子微调排序 → 按数据量决定是否做二元切分或上外部搜索引擎。索引表要纳入备份策略的考虑范围,批量导入数据后记得重建索引。搜索结果的呈现(比如把产品排在前面)也可以配合 相关文章推荐方案 一起优化;而如果连默认搜索为什么慢都还不清楚,先补 中文搜索不好用的根因 这一课。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。