How to completely exclude a specific post ID from WordPress and sync it to Meilisearch (Scry Search Plugin in Practice)

I've been working on something lately.WordpressI wanted to add a high-performance site-wide search to my website. After much searching, I chose Meilisearch and paired it with a plugin called Scry Search.

It went smoothly at first. I installed the plugin, configured it, and the index was generated automatically. The search speed was lightning fast, and the experience was indeed excellent.

But here is the problem.

There are several unusual articles on the website. Some are for internal testing, some are dedicated landing pages for specific clients, and others are unfinished content that we don't want to delete. I need these article IDs to completely disappear from search results.

It's not enough that it can't be found; it's that it can't even be in Meilisearch's index.

How to completely exclude a specific post ID from WordPress and sync it to Meilisearch (Scry Search Plugin in Practice)

I thought this was simple; it's just a matter of excluding a few IDs. The plugin documentation must have the corresponding hooks; just add a filter and it's done.

As it turned out, I was wrong.

Why do conventional interception methods fail?

I first tried the filter hook mentioned in the official documentation. I added a few lines of code to functions.php, saved it, refreshed the backend, and re-indexed it.

Then I checked the Meilisearch backend.

Those articles are still there.

I was stunned.

I thought I had written the wrong code, so I checked it several times, but there was nothing wrong with it. Then I searched the plugin's GitHub Issues and found that several other people had encountered similar problems.

It turns out that the Scry Search plugin uses an asynchronous task queue mechanism to avoid slowing down the background saving process. This means that when you click "Save Article" in the background, the data may be instantly pushed into a custom task table, bypassing the regular single-article filtering.

What's even more insidious is that when you click "Index Posts" in the background to regenerate the global index, the plugin directly performs a batch database query at the underlying level. At this point, the filter hook you added earlier never gets a chance to execute.

In other words, conventional blocking methods only take effect the moment you manually save the article. But Scry Search's synchronization logic is far more complex than you might imagine.

Double-insurance interceptor core code

After thinking about it, I realized this matter couldn't be left like this.

Since the conventional hook only controls the "single-article saving" entry point, I need to find a way to block it from other places as well.

I've come up with two paths.

The first issue is the network transmission end. Whether it's single-article synchronization or batch synchronization, the final data still needs to be sent to the Meilisearch server via HTTP requests, right? So, before sending the HTTP request, I should check if the request body contains the IDs of those excluded articles. If so, I should directly block the request from being sent.

The second point concerns the database query. Since the plugin retrieves the article list directly from the database during batch indexing, I'll remove those specific IDs from the query results before executing the database query. From the plugin's perspective, these articles don't exist at all, so they won't be retrieved.

Two paths, double insurance. If one path can't be blocked, there's another as a backup.

After figuring it out, I started writing code.

For the first interceptor, I used WordPress's native one.pre_http_requestThe filter. This hook triggers before WordPress makes any HTTP requests. My logic is that it will detect if the requested URL contains...meilisearchIf the request body contains the IDs of the excluded articles, the request will be blocked.

To prevent the plugin from reporting errors, I also need to fake a successful response. The standard response format for Meilisearch is...{"taskUid":0,"status":"enqueued"}I simply returned this, making the plugin think that the synchronization was successful.

The second interceptor I usedpre_get_postsThe hook. This hook is triggered before WordPress executes a database query. My logic is that whenever an administrator performs an operation in the backend, or when a plugin performs asynchronous/synchronous operations, the excluded IDs should be merged into the...post__not_inIn the parameters.

After I finished writing it, I tested it.

First, I went to the backend, opened one of the excluded articles, made a few minor changes, and clicked update. It saved successfully without any errors. Then I checked the Meilisearch backend, and the article's index remained unchanged; nothing new had appeared.

I clicked "Index Posts" again and rebuilt the entire index. After waiting a while, I checked the Meilisearch backend. Those excluded articles were still there.

It became.

In-depth analysis: How does the double insurance mechanism work?

To be honest, this process reminded me of something quite interesting.

You know, in the 1880s, when electricity was just becoming widespread in the United States, many factory owners spent a lot of money to buy generators and electric motors and install them in their factories. However, after installation, many people found that production efficiency did not improve significantly.

why?

Because they simply replaced the steam engine with an electric motor, but the overall layout, processes, and management methods of the factory remained unchanged. The electricity was new, but the mindset for using it was old.

Those who truly benefited from the electricity boom were actually the first group to understand "what electricity really means." They didn't just change their power source; they redesigned their entire production process.

现在AIThe same applies to the times. Many people use AI as a tool, but few consider what underlying logic it actually changes. The tool may be new, but the mindset used to use it may remain outdated.

For example, when I was setting up WordPress search blocking, I probably wouldn't have been able to get it working if I just followed the standard methods in the plugin documentation. This is because Scry Search's synchronization logic is no longer the traditional "save one article, synchronize one article" approach; it has asynchronous queues, batch processing, and its own set of mechanisms.

You need to figure out how this mechanism works before you can find a real breakthrough.

Limitations and precautions of the plan

However, I must frankly say that this plan is not perfect either.

It has a significant limitation: it can only manage future synchronizations and cannot automatically erase existing historical records in Meilisearch. In other words, if you have already synchronized those articles, you still need to manually delete the old data in the Meilisearch dashboard or using API commands.

This is a one-time task; once it's done, you don't need to worry about it anymore. But I need to make this clear beforehand, so that after you deploy the code, you don't find those articles still in the search results and assume the code hasn't taken effect.

Another point to note is that this approach relies on WordPress's underlying network and database architecture. As long as future versions of the Scry Search plugin continue to operate based on this architecture, this interceptor will remain effective. However, if it ever switches to a completely different synchronization mechanism, then re-adaptation may be necessary.

To be honest, however, this is unlikely. The entire WordPress ecosystem is built on this architecture, and it's virtually impossible for plugins to completely bypass it.

Three-Step Guide to Implementation

/**
 * 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));
    }
});

Finally, let's summarize the operation steps.

First, copy the code to the very bottom of your functions.php file, or add it using the Code Snippets plugin. Then, at the top...defineIn the array, enter the ID of the article or page you want to exclude.

The second step is to clean up historical indexes. Log in to your Meilisearch dashboard, or use API commands to manually delete the old index data for the excluded articles.

The third step is testing. Go to the backend and make any minor changes to the excluded article, click update, and then check the Meilisearch backend. If no new index appears, the blocking has taken effect.

To be honest, I've always felt a bit guilty about writing these kinds of technical sharing articles.

The things I share may be useful to some people, but for others they may just be basic operations.

But the process of implementing WordPress search blocking this time was truly insightful. Often, the problems we encounter aren't due to a lack of solutions, but rather because our thinking is limited by existing frameworks.

The Scry Search plugin provides a filtering hook, leading us to believe that this is the only option. However, the entire architecture of WordPress offers far more possibilities. Interception can be achieved at the network layer, and it can also be done at the database layer. As long as you're willing to think, there's always a way.

That's why I enjoy tinkering with these technical gadgets. It's not about showing off or trying to appear impressive. It's simply because the feeling of completely understanding a problem is incredibly satisfying.

Just like this time, from the initial confusion, to the reflection in the middle, to the final solution, the whole process was like solving a puzzle.

The mystery has been solved, the answer has been revealed, it turns out it was so simple.

But if you haven't gone through that confusing process, you'll never understand this simple joy.

Now that you've read this far, if you found it helpful, please like and share it. If you want to receive updates first, you can also follow me.

Thank you for reading my article. See you next time.

Comment

Your email address will not be published. Required fields * Callout

Scroll to Top