WordPressの深淵へ:`post_content_filtered` を使った検索アーキテクチャの最適化
WordPressの検索機能、使っていて「遅いな」と感じたことはありませんか?
標準の `WP_Query` は強力ですが、`wp_posts` テーブルの `post_content` に対して `LIKE` 検索を行うため、データ量が増えるほどデータベースの負荷は指数関数的に跳ね上がります。
今回は、WordPressのコアテーブルにひっそりと存在する、ほとんどの人が活用していない「魔法のカラム」である `post_content_filtered` を使った、プロフェッショナルな検索負荷分散術を解説します。
—
1. なぜ `post_content_filtered` なのか?
WordPressのデータベース構造を覗くと、`wp_posts` テーブルには `post_content` とは別に `post_content_filtered` というカラムがあります。
実はこれ、「処理済みのコンテンツを保存するための予備領域」として定義されています。しかし、現代のWordPress開発において、このカラムをそのまま「処理済みデータ」として使うのはナンセンスです。
私たちはここを「外部全文検索エンジン(ElasticsearchやAlgoliaなど)への同期用ゲートウェイ」として再定義します。
図解:検索負荷分散のアーキテクチャ
[WordPress]
|– コンテンツ更新 (save_post)
|– [post_content_filtered] に「検索用正規化データ」を書き込む
|– [Sync Handler] が変更を検知
|– [外部検索エンジン] (Elasticsearch等) へデータを転送
|– 検索リクエストは全て外部エンジンが処理
こうすることで、MySQLの重い `LIKE` 検索を完全に回避し、爆速の全文検索環境を構築できます。
—
2. 実装:フックを使ったデータ同期の自動化
まずは、記事が保存されるタイミングで、`post_content_filtered` に検索用の「クリーンなテキスト」を格納するコードを見ていきましょう。
/
- 記事保存時に検索用データを生成してカラムに保持する
/
add_action(‘save_post’, ‘sync_to_filtered_column’, 10, 3);
function sync_to_filtered_column($post_id, $post, $update) {
// リビジョンや自動保存は無視
if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
return;
}
// HTMLタグを除去し、検索エンジンが解析しやすいテキストに変換
$search_text = strip_tags($post->post_content);
$search_text = preg_replace(‘/\s+/’, ‘ ‘, $search_text); // 余計な空白を詰める
// global $wpdb を使って直接更新する(パフォーマンスのため)
global $wpdb;
$wpdb->update(
$wpdb->posts,
[‘post_content_filtered’ => $search_text],
[‘ID’ => $post_id],
[‘%s’],
[‘%d’]
);
}
ここがプロのポイント
- `wp_is_post_revision` のチェック: 初学者がよくやるミスが「リビジョン保存時に無限ループでフックが走る」ことです。必ず防衛線を張りましょう。
- `$wpdb->update` の活用: `wp_update_post` を使うと再び `save_post` フックが走り、無限ループの地獄に落ちます。低レイヤーである `$wpdb` を直接叩くのが、コアを知るエンジニアの作法です。
—
3. 検索の主役を外部エンジンへ譲り渡す
`post_content_filtered` にデータがあることで、外部エンジン(Elasticsearch等)との同期が非常に楽になります。外部のクローラーは、`wp_posts` 全体をスキャンするのではなく、「更新されたIDの `post_content_filtered` だけを読みに行けばいい」からです。
以下は、外部検索エンジンへデータをプッシュする際のイメージです。
// 外部APIへデータを飛ばす擬似コード
function push_to_external_search_engine($post_id) {
$content = get_post_field(‘post_content_filtered’, $post_id);
// APIクライアントを使って検索エンジンにインデックス
// $client->index([‘body’ => $content, ‘id’ => $post_id]);
}
—
4. 初学者が陥りやすい罠
1. カラムの型制限: `post_content_filtered` は `longtext` 型ですが、過剰なデータを詰め込みすぎると、特定の環境でクエリキャッシュが効きづらくなる場合があります。あくまで「検索に必要なメタデータ」に絞りましょう。
2. フックの実行順序: `save_post` はカスタムフィールドの保存後に行われます。もしカスタムフィールドの内容も検索対象にしたい場合は、`save_post` ではなく `post_updated` フックや、より優先度の低いアクションを検討する必要があります。
3. データベースのロック: 大量更新時、`$wpdb->update` を頻繁に叩くと一時的にテーブルがロックされる可能性があります。バッチ処理を行う場合は、一度に全件更新せず、`wp_schedule_event` を使って分割処理することをお勧めします。
—
まとめ
`post_content_filtered` は、WordPressという古き良きフレームワークが残してくれた、現代の分散アーキテクチャのための「隠し扉」のようなものです。
1. 標準の `wp_posts` の重い検索に頼らない。
2. `post_content_filtered` を正規化データ置き場として活用する。
3. 外部エンジンとの同期を非同期化する。
このステップを踏むだけで、あなたのWordPressサイトは数百万記事規模のトラフィックにも耐えうる、プロフェッショナルなインフラへと進化します。
「ここをクリアすれば、WordPressの基本はバッチリマスター」です。次は、この `post_content_filtered` を使ったカスタムクエリの最適化に挑戦してみませんか?応援していますよ。