wp_postsの呪縛を解け:全文検索のボトルネックを解消する「外部オフロード」のアーキテクチャ
WordPressの `wp_posts.post_content` は、いわば「ゴミ溜め」に近い。リッチテキスト、ショートコード、埋め込みタグが混沌と混ざり合うこのカラムに対し、`LIKE ‘%keyword%’` を用いたSQLクエリを発行するのは、現代のWebアプリケーションにおいて「死」を意味する。
テーブルスキャン(全件走査)が発生し、データベースのI/Oを枯渇させ、結果としてサイト全体がスローダウンする。我々エンジニアがやるべきことは、「WPのコアを破壊せずに、検索責任を剥離すること」だ。
今回は、この呪縛から逃れるための「オフロード設計」と、実務で耐えうる堅牢な実装パターンを伝授する。
—
1. なぜ「SQL LIKE検索」は敗北するのか
WordPressのデータ構造上、`wp_posts` は万能だが、検索には向いていない。
- インデックスの不在: `post_content` は `LONGTEXT` 型であり、B-Treeインデックスが効かない。
- 非正規化の弊害: 全文検索のためにフルスキャンが走ると、`wp_options` や `wp_postmeta` のキャッシュヒット率まで悪影響を受ける。
我々が目指すべきは、「検索エンジンへのデータストリーミング(非同期)」と「読み取り専用インデックスの分離」である。
—
2. アーキテクチャ戦略:同期と非同期の分離
運用負荷とパフォーマンスのバランスを取るため、以下の構成を推奨する。
1. データストア: ElasticSearch (または Algolia / Meilisearch) を採用する。
2. データ抽出: `save_post` フックをトリガーに、非同期(Action Scheduler)でインデックスを更新する。
3. 検索実行: WP_Query を経由せず、REST API または直接インデックスエンジンへクエリを投げる。
—
3. 実践コード:Action Schedulerを用いた堅牢なオフロード
`save_post` で直接APIを叩いてはいけない。リクエストがタイムアウトした場合、投稿保存自体が失敗する可能性があるからだ。`Action Scheduler` を使い、バックグラウンド処理としてキューイングする。
/
- 投稿保存時に検索用インデックスへデータを同期する設計
/
add_action(‘save_post’, function ($post_id, $post, $update) {
// リビジョンや自動保存をフィルタリング(必須)
if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
return;
}
// 非同期キューへタスクを登録(Action Scheduler)
// これにより、保存処理のレスポンスをブロックしない
as_enqueue_async_action(‘sync_post_to_external_search’, [$post_id]);
}, 10, 3);
/
- 非同期ワーカー:外部検索エンジンへのデータ送信
/
add_action(‘sync_post_to_external_search’, function ($post_id) {
$post = get_post($post_id);
// post_contentの不要なショートコードやHTMLタグを除去して正規化
$clean_content = wp_strip_all_tags(do_shortcode($post->post_content));
$data = [
‘id’ => $post_id,
‘title’ => $post->post_title,
‘content’ => $clean_content,
‘url’ => get_permalink($post_id),
];
// ここで外部API(ElasticSearch等)へPOSTリクエストを送信
$response = wp_remote_post(‘https://search-engine.internal/index’, [
‘body’ => json_encode($data),
‘headers’ => [‘Content-Type’ => ‘application/json’],
‘timeout’ => 5, // APIの遅延に引きずられないようタイムアウトを厳格に設定
]);
});
—
4. プロダクション環境での重要注意点
A. データ整合性の担保(Reconciliation)
非同期処理は「失敗」する可能性がある。外部インデックスとDBのデータが乖離し始めた際、数万件のデータを再同期させるための CLIコマンド(`wp-cli`)を必ず実装しておくこと。
// WP-CLIで全データ再同期
if (defined(‘WP_CLI’) && WP_CLI) {
WP_CLI::add_command(‘search-sync’, function() {
$posts = get_posts([‘posts_per_page’ => -1]);
foreach ($posts as $post) {
as_enqueue_async_action(‘sync_post_to_external_search’, [$post->ID]);
}
WP_CLI::success(‘Sync queued.’);
});
}
B. コンテンツの正規化(Normalizer)
`wp_strip_all_tags` だけでは、埋め込みコードやCSS/JSの断片が残る。検索精度を上げるには、`strip_shortcodes()` を適用した上で、さらに `preg_replace` で不要な記号を排除する独自の Normalizer 関数を噛ませるべきだ。検索エンジンに「ゴミ」を食わせると、ノイズだらけの検索結果しか得られない。
—
5. 結論
WordPressを「データベース」として使い続けることは、ある一定の規模を超えると限界が来る。`wp_posts` の中のデータは、「表示用」と割り切り、検索という「検索用」の責務は専用エンジンに委譲する。
この設計思想を適用するだけで、データベースの負荷は劇的に下がり、検索速度はミリ秒単位まで短縮される。これこそが、コアコントリビューターが現場で用いる「WordPressを支配する技術」だ。
次にコードを書くとき、`LIKE` を使おうとする己の指を止めてほしい。そのクエリは、本当に必要か? インデックスを分離する準備はできているか? それこそが、一流のエンジニアへの分水嶺である。