wp_posts.post_content_filtered の真実:MySQL演算コストを排除し、全文検索を最適化するアーキテクチャ設計
WordPressのデータベース構造を俯瞰したとき、多くのエンジニアが犯す最大の過ちは、`wp_posts.post_content` を検索の直接的なターゲットとして `LIKE` 句を投げることだ。これはRDBMSに対する冒涜であり、大規模トラフィック環境では確実にI/O Waitのボトルネックとなる。
今回は、WordPressコアにおいて「忘れられたカラム」である `post_content_filtered` を利用し、検索負荷をアプリケーション層から切り離すアーキテクチャ戦略を解説する。
—
1. なぜ post_content への直接クエリが「死」を招くのか
`post_content` は `LONGTEXT` 型であり、インデックスを貼ることは不可能(あるいは極めて非効率)だ。MySQLは検索のたびにフルテーブルスキャン、あるいは少なくともクラスタ化インデックスの全走査を強制される。
多くのプラグインが実装する「検索用メタテーブル」の生成は、`wp_postmeta` を肥大化させ、JOINのコストを増大させるという負のループに陥る。ここで、WordPressがコアレベルで用意しているが、事実上未使用である `post_content_filtered` を活用する。
なぜ post_content_filtered なのか
- 物理的近接性: `wp_posts` テーブル内に存在するため、JOINが不要。
- データ型: 同様に `LONGTEXT` だが、セマンティックな分離が可能。
- キャッシュ効率: MySQLのバッファプールにおいて、行データの一部としてキャッシュに乗る確率が高まる。
—
2. データ同期戦略:イベント駆動型パイプライン
`save_post` フックをトリガーに、非同期で検索用の正規化されたコンテンツを生成・格納する。ここで重要なのは、「メインスレッドでの計算を避ける」ことだ。
以下のコードは、`post_content` からHTMLタグを除去し、検索エンジン(ElasticsearchやAlgolia、あるいはMySQLのFULLTEXTインデックス)に最適化された形式を `post_content_filtered` に書き込む例である。
/
- 検索最適化のためのデータ同期パイプライン
- 実行コンテキスト: save_post
/
add_action(‘save_post’, function($post_id, $post, $update) {
// 再帰呼び出しを防止し、非同期またはバックグラウンドプロセスでの実行を推奨
if (defined(‘DOING_AUTOSAVE’) && DOING_AUTOSAVE) return;
// コンテンツの正規化(HTMLタグ除去、エンティティデコード)
$clean_content = wp_strip_all_tags($post->post_content);
$clean_content = html_entity_decode($clean_content, ENT_QUOTES, ‘UTF-8’);
// 直接的なSQL実行によるオーバーヘッドの最小化
global $wpdb;
$wpdb->update(
$wpdb->posts,
[‘post_content_filtered’ => $clean_content],
[‘ID’ => $post_id],
[‘%s’],
[‘%d’]
);
}, 10, 3);
—
3. 全文検索エンジンとのブリッジ
`post_content_filtered` を利用する真の意義は、外部検索エンジン(Elasticsearch等)へのインデックス投入時の「中間バッファ」として機能させることにある。
外部インデックスの同期失敗時、`wp_posts` 内に正規化されたデータが存在することで、リカバリプロセスを簡略化できる。また、MySQL 5.7+ / 8.0 の `FULLTEXT` インデックスを `post_content_filtered` にのみ適用することで、検索速度は劇的に向上する。
— テーブル構造の最適化(低レイヤレベルでの施策)
ALTER TABLE wp_posts ADD FULLTEXT INDEX ft_index_filtered (post_content_filtered);
— 検索クエリの最適化
— LIKE ‘%…%’ ではなく MATCH … AGAINST を使用する
SELECT ID, post_title FROM wp_posts
WHERE MATCH(post_content_filtered) AGAINST(‘キーワード’ IN BOOLEAN MODE);
—
4. チーフアーキテクトからの助言:メモリと実行順序
システムを掌握するためには、以下の2点に注意せよ。
1. メモリ最適化: `post_content` が巨大な場合、`wp_strip_all_tags` はメモリを食いつぶす。大規模なサイトであれば、PHPの `DOMDocument` を使用せず、正規表現ベースのクリーナーをC言語レベル(またはPHPのストリームフィルタ)で実装し、メモリ使用量を抑えるべきだ。
2. 実行順序の制御: `save_post` は `wp_insert_post` の直後に実行されるが、他のプラグインが同様の処理を行っている場合、競合が発生する。優先度を `PHP_INT_MAX` に設定し、最後に処理を確定させるのが定石だ。
結論
`post_content_filtered` は、WordPressが我々に提供した「最後の聖域」である。このカラムを適切に管理することで、アプリケーション層での複雑なクエリ生成から解放され、データベースエンジン本来のポテンシャルを引き出すことが可能となる。
アーキテクチャの真髄は、機能を追加することではなく、物理層における負荷を数学的な最適解へ導くことにある。コードを書く前に、まずテーブルのビット単位の動きを想像せよ。それが伝説的なエンジニアへの第一歩だ。