Нийтлэлийн лавлах
我最近在折腾一个WordPress网站,想给它加个高性能的全站搜索。选来选去,用了Meilisearch,然后搭配了一个叫Scry Search的插件。
一开始挺顺利的,插件装上,配置一下,索引就自动生成了。搜索速度嗖嗖的,体验确实不错。
Гэхдээ энд асуудал гарч ирдэг.
网站里有几篇很特殊的文章。有的是内部测试用的,有的是给特定客户看的专属落地页,还有的是还没打磨好但不想删掉的内容。这些文章的ID,我需要它们彻底从搜索结果里消失。
不是搜不到就行,是连Meilisearch的索引里都不能有。

我以为这事很简单,不就是排除几个ID嘛。插件文档里肯定有对应的钩子,加个filter不就完事了。
结果,我错了。
为什么常规的拦截方法会失效?
我先是试了官方文档里提到的那个过滤钩子。在functions.php里加了几行代码,保存,刷新后台,重新索引。
然后去Meilisearch后台一看。
那几篇文章,还在那儿呢。
我当时就愣住了。
我以为是我代码写错了,检查了好几遍,没毛病啊。然后我又去插件的GitHub Issues里搜了一圈,发现有好几个人遇到过类似的问题。
原来Scry Search这个插件,为了不拖慢后台的保存速度,搞了一套异步任务队列机制。就是说,当你在后台点”保存文章”的时候,数据可能瞬间就被推进了自定义的任务表,绕过了常规的单篇过滤。
更骚的是,当你在后台点”Index Posts”想重新生成全局索引的时候,插件会直接走底层的批量数据库查询。这时候,你之前加的那个过滤钩子,根本就没机会执行。
等于说,常规的拦截方法,只在你手动保存文章的那一刻生效。但Scry Search的同步逻辑,远比你想象的复杂。
双保险拦截器核心代码
我寻思了一下,这事不能这么算了。
既然常规钩子只管”单篇保存”这个入口,那我就得想办法,从别的地方也堵上。
我想到了两个路径。
第一个是网络传输端。不管是单篇同步还是批量同步,最终数据都得通过HTTP请求发给Meilisearch服务器吧?那我在HTTP请求发出去之前,先检查一下请求体里有没有包含那几个被排除的文章ID。如果有,直接掐断,不让请求发出去。
第二个是数据库查询端。既然批量索引的时候,插件会直接从数据库里捞文章列表,那我就在数据库查询执行之前,把那几个ID从查询结果里剔除掉。在插件看来,这几篇文章压根就不存在,自然也就不会被捞出来。
两个路径,双保险。一条路堵不死,还有另一条兜底。
想明白之后,开始写代码。
第一个拦截器,我用了WordPress原生的pre_http_request过滤器。这个钩子会在WordPress发出任何HTTP请求之前触发。我的逻辑是,只要发现请求的URL里包含meilisearch,并且请求体里包含了那几个被排除的文章ID,就直接拦截这次请求。
为了让插件那边不报错,我还得伪造一个成功的响应。Meilisearch的标准响应格式是{"taskUid":0,"status":"enqueued"},我直接返回这个,让插件以为同步成功了。
第二个拦截器,我用了pre_get_posts钩子。这个钩子在WordPress执行数据库查询之前触发。我的逻辑是,只要是在后台管理员操作,或者是在插件执行异步同步的时候,就把那几个被排除的ID合并到post__not_in参数里。
写完之后,我测试了一下。
先去后台,打开其中一篇被排除的文章,随便改了几个字,点更新。保存成功,没有任何报错。然后去Meilisearch后台一看,那篇文章的索引,还是老样子,没有多出来。
我又去点了一下”Index Posts”,全量重新构建索引。等了一会儿,去Meilisearch后台检查。那几篇被排除的文章,依然干干净净。
成了。
深度解析:双保险机制是如何工作的?
说实话,这个过程让我想起了一件挺有意思的事。
你知道1880年代,电力刚在美国普及的时候,很多工厂主花大价钱买了发电机和电动机,装在自己的工厂里。但是装完之后,很多人发现生产效率并没有显著提升。
яагаад?
因为他们只是用电动机替代了蒸汽机,但整个工厂的布局、流程、管理方式都没有变。电力是新的,但使用电力的思维方式还是旧的。
后来那些真正吃到电力红利的人,其实是最早想明白”电力到底意味着什么”的那波人。他们不只是换了个动力源,而是重新设计了整个生产流程。
саяханAI时代也是一样。很多人把AI当成一个工具来用,但很少有人去想,AI到底改变了什么底层逻辑。工具是新的,但使用工具的思维方式可能还是旧的。
就像我这次搞WordPress搜索拦截,如果我只是按照插件文档里的常规方法去做,大概率是搞不定的。因为Scry Search的同步逻辑已经不是传统的”保存一篇文章就同步一篇文章”了,它有异步队列,有批量处理,有自己的一套机制。
你得先搞明白这套机制是怎么运转的,然后才能找到真正的突破口。
方案的局限性与注意事项
不过我得坦率地说,这套方案也不是完美的。
它有一个很明显的局限,就是只能管住未来的同步,没法自动抹去Meilisearch里已经存在的历史记录。也就是说,如果你之前已经同步过那几篇文章,那你还得手动去Meilisearch的仪表盘或者用API命令,把那些旧数据删掉。
这是个一次性的工作,做完了就不用再管了。但我得提前说清楚,免得你部署完代码之后,发现那几篇文章还在搜索结果里,然后以为代码没生效。
还有一个点需要注意,就是这套方案依赖于WordPress的底层网络和数据库架构。只要Scry Search插件未来更新的版本还是基于这套架构运行,这套拦截器就一直有效。但如果它哪天改用了完全不同的同步机制,那可能就需要重新适配了。
不过说实话,这种可能性不大。WordPress的整个生态都是建立在这套架构之上的,插件想完全绕开,几乎不可能。
落地使用三步走指南
/**
* Scry Search 双保险拦截器:彻底排除指定文章 ID 同步到 Meilisearch
*/
// ==========================================
// 【配置】请在这里填写你想要排除的文章或页面 ID
// ==========================================
define('MEILI_EXCLUDED_IDS', array(1067, 1014, 1474, 34020));
/**
* 保险一:拦截单篇保存/更新时的网络推送 (方案 A 优化版)
* 原理:当 WordPress 发送网络请求时,如果发现是发往 meilisearch 且带有排除的 ID,直接掐断
*/
add_filter('pre_http_request', function($preempt, $parsed_args, $url) {
// 1. 检查是不是发往 Meilisearch 的请求
if (strpos($url, 'meilisearch') !== false && isset($parsed_args['body'])) {
$body_content = $parsed_args['body'];
// 2. 检查请求体里是否包含任何一个被排除的文章 ID
foreach (MEILI_EXCLUDED_IDS as $id) {
// 匹配格式如 "id":1067 或 字符串中的 ID
if (strpos($body_content, (string)$id) !== false) {
// 找到匹配,直接拦截网络请求,并向插件伪造一个标准的成功响应
return array(
'response' => array('code' => 200, 'message' => 'OK'),
'body' => '{"taskUid":0,"status":"enqueued"}'
);
}
}
}
return $preempt;
}, 10, 3);
/**
* 保险二:拦截后台全量索引时的数据库查询 (方案 B)
* 原理:在插件试图从数据库捞取文章列表时,直接将这几个 ID 从查询结果中剔除
*/
add_action('pre_get_posts', function($query) {
// 仅在后台管理员操作,或者插件执行异步同步时生效
if (is_admin() || (defined('DOING_ASYNC') && DOING_ASYNC)) {
// 获取当前查询已经存在的排除 ID(如果有的话)
$current_excluded = $query->get('post__not_in');
if (!is_array($current_excluded)) {
$current_excluded = array();
}
// 将我们的专属排除 ID 合并进去
$query->set('post__not_in', array_merge($current_excluded, MEILI_EXCLUDED_IDS));
}
});
最后,总结一下操作步骤。
第一步,把代码复制到你的functions.php文件最底部,或者用Code Snippets插件添加。然后在顶部的define数组里,填上你想要排除的文章或页面ID。
第二步,清理历史索引。登录你的Meilisearch仪表盘,或者用API命令,手动删除那几篇被排除文章的旧索引数据。
第三步,测试。去后台随便改一下被排除的文章,点更新,然后去Meilisearch后台检查。如果没有多出来新的索引,说明拦截已经生效了。
说实话,写这种技术分享类的文章,我一直是有心理负担的。
因为我分享的这些东西,可能对某些人有用,但对另一些人来说,可能就是基本操作。
但这次搞WordPress搜索拦截的过程,确实让我挺有感触的。很多时候,我们遇到的问题,不是没有解决方案,而是我们被现有的框架限制住了思路。
Scry Search插件提供了过滤钩子,我们就以为只能用这个钩子。但其实,WordPress的整个架构,给了我们更多的可能性。网络层可以拦截,数据库层也可以拦截。只要你愿意去想,路总是有的。
这也是我为什么喜欢折腾这些技术玩意的原因。不是为了炫技,也不是为了显得自己很厉害。就是单纯地觉得,当你把一个问题彻底搞明白的时候,那种感觉太爽了。
就像这次,从一开始的困惑,到中间的思考,到最后的解决。整个过程,就像在解一个谜题。
谜题解开了,谜底揭晓了,原来这么简单。
但如果你没经历过那个困惑的过程,你永远体会不到这种简单的快乐。
以上,既然看到这里了,如果觉得不错,随手点赞、转发吧,如果想第一时间收到推送,也可以给我个关注。
Миний нийтлэлийг уншсанд баярлалаа. Дараагийн удаа уулзъя.
Hope Chen Weiliang блог ( https://www.chenweiliang.com/ ) 分享的《WordPress如何彻底排除指定文章 ID 同步到 Meilisearch(Scry Search 插件实战)》,对您有帮助。
Энэ нийтлэлийн холбоосыг хуваалцахад тавтай морилно уу:https://www.chenweiliang.com/cwl-34343.html
