Tips
WordPressのACFカスタムフィールドをRelevanssiで全文検索対象にした改修記録
背景と問題の整理
対象サイトは不動産系のWordPressサイト。物件情報・担当者プロフィール・エリアコラムといった複数の投稿タイプを持ち、データの大部分をACF(Advanced Custom Fields)のカスタムフィールドに格納していました。
WordPressのデフォルト検索が参照するのはwp_postsテーブルのpost_titleとpost_contentの2カラムのみだ。カスタムフィールドの値はwp_postmetaに格納されており、標準のWP_Queryによる検索からは完全に除外される。その結果、ユーザーが物件名や沿線名で検索しても何もヒットしないという状態になっていました。
解決策としてはいくつか考えられます。
WP_Queryにmeta_queryを追加してカスタムフィールドを検索対象に含めるposts_searchフィルターをフックしてSQLを拡張する- Relevanssiのような全文検索プラグインに置き換える
meta_queryの拡張は検索フィールドが増えるたびにSQLが肥大化するうえ、スコアリングができない。今回は検索精度の向上も求められていたため、Relevanssiを採用しました。
Relevanssiの概要
RelevanssiはWordPressの検索クエリをpre_get_postsフィルター等でフックし、デフォルトのLIKEベースのSQLを自前の全文検索エンジンに置き換えるプラグインです。検索結果はTF-IDF的なスコアリングでランキングされます。
主な仕様として押さえておくべき点は以下のとおり。
- インデックスは専用テーブル(
wp_relevanssi)に構築される - カスタムフィールド・カスタム投稿タイプ・タクソノミーをインデックス対象に追加できる
- タイトル・本文・カスタムフィールドごとにスコアの重みを個別設定できる
- 無料版(Relevanssi)と有料版(Relevanssi Premium)がある。PDFインデックスやユーザーロール別の検索制御が必要な場合はPremiumが必要
Step 1:インストールとインデックス初期構築
プラグインのインストール自体は通常の手順で問題ないです。有効化後、管理画面の「Relevanssi」メニューが追加されます。
初回インデックス構築は設定画面の「Build the index」から実行できますが、投稿数が数千件規模の場合はHTTPリクエストのタイムアウトにより途中で処理が中断されることがあります。この場合はWP-CLIを使うのが確実です。
wp relevanssi-index
オプションで--network(マルチサイト対応)や--setup(設定のリセットを伴う再構築)も指定できます。投稿数が多い環境では最初からWP-CLIを使うことを推奨します。
なお、インデックス構築後に設定を変更した場合は再インデックスが必要になります。設定変更だけではインデックスは更新されない点に注意しましょう。
Step 2:ACFカスタムフィールドのインデックス設定
Relevanssiの「Indexing」タブに「Custom fields」という設定項目があります。デフォルト値はdon't indexで、選択肢は以下のとおり。
| 値 | 挙動 |
|---|---|
don't index | カスタムフィールドを検索対象外にする(デフォルト) |
visible fields | _始まりの隠しフィールドを除くすべてをインデックス |
all fields | すべてのカスタムフィールドをインデックス |
| フィールド名(カンマ区切り) | 指定したフィールドのみインデックス |
visible fieldsやall fieldsを選ぶと、ACFが内部的に使用する管理用フィールド(フィールド定義の参照キーなど)もインデックスに含まれてしまい、検索ノイズの原因になる場合があります。今回は検索対象とするフィールドが明確だったため、フィールド名を個別指定しました。
property_name, train_line, area_description, features_text
ACFのフィールドキーはフィールドグループの設定画面で確認できます。wp_postmetaのmeta_keyと一致しているか、get_post_meta()で実際の値が取れることを確認しておくと確実です。
設定保存後、WP-CLIで再インデックスを実行します。
wp relevanssi-index
Step 3:日本語環境への対応
Relevanssiの形態素解析はデフォルトでスペース区切りを前提としており、日本語の連続したテキストはそのまま1トークンとして扱われます。WordPressのロケールがjaであれば最低限の対応はされますが、完全な分かち書きには対応していません。
高精度な日本語全文検索が要件に含まれる場合は、MeCabやKuromojiを組み合わせた別のアプローチ(例:Elasticsearch + WP_Elasticserach)を検討すべきです。
今回のケースでは、ユーザーのクエリが「渋谷 1LDK」「駅近 ペット可」のように単語をスペースで区切って入力するパターンが主であったため、Relevanssiの標準設定で十分な精度が出ました。複雑な自然言語クエリが求められる要件ではない点も考慮した判断でした。
Step 4:スコアリングの重み付けとデフォルト検索モードの設定
「Weights」セクションでタイトル・本文・カスタムフィールドそれぞれのスコア重みを設定できます。今回は以下の値に調整しました。
| 対象 | 重み |
|---|---|
タイトル(post_title) | 10 |
本文(post_content) | 1 |
| カスタムフィールド | 0.5 |
物件タイトルへのマッチを最優先とし、カスタムフィールドはタイトルにない補足情報として位置づけました。重みの調整は実際の検索クエリに対して結果を確認しながら行う必要があります。一度設定したら終わりではなく、本番後もチューニングする前提で臨むべき項目です。
また、デフォルトの検索モードはOR検索ですが、「渋谷 ペット可 2LDK」のような複数キーワードでの絞り込みがユースケースの中心だったため、AND検索をデフォルトに変更した。この設定はfunctions.phpでフィルターを使って制御することもできます。
add_filter( 'relevanssi_default_operator', function() {
return 'AND';
} );
Step 5:テンプレートの修正
Relevanssiはpre_get_postsをフックするため、既存のsearch.phpテンプレートはほぼそのまま使用できます。WP_Queryのパラメータ変更も不要です。
変更が必要だった箇所は検索結果のスニペット表示のみでした。Relevanssiはrelevanssi_the_excerpt()という専用関数を提供しており、これを使うことでマッチしたキーワードを含む周辺テキストをハイライト付きで出力できます。
search.php内のthe_excerpt()を以下に置き換えます。
relevanssi_the_excerpt();
ハイライトのCSSクラスはデフォルトで<span class="relevanssi-query-term">が付与される。スタイルは別途CSSで定義します。
Step 6:ステージング環境での検証
本番デプロイ前に、ステージング環境で以下の項目を確認しました。
インデックスの確認
- 対象カスタムフィールドの値がインデックスに含まれているか(管理画面の「Relevanssi」→「Searching」→「Debugging」から確認可能)
wp_relevanssiテーブルにレコードが生成されているか
検索動作の確認
- 各カスタムフィールドに含まれるキーワードで正しくヒットするか
- インデックス対象外のフィールド値ではヒットしないか
- 存在しないキーワードで0件が返るか
- AND/OR検索の挙動が設定と一致しているか
- 投稿タイプ別に結果が意図どおりに出力されるか
投稿タイプのフィルタリング
Relevanssiの「Indexing」タブで「Post types to index」を設定することで、インデックス対象の投稿タイプを絞り込める。今回はスタッフ紹介(staff)を除外し、物件(property)とコラム(column)のみを対象としました。
Step 7:本番デプロイとインデックス再構築
本番環境へのデプロイ後、ステージングと同様の設定を適用したうえでWP-CLIでインデックスを再構築しました。
wp relevanssi-index
本番の投稿数はステージングより多かったが、WP-CLI経由であれば問題なく完了しました。デプロイ後は検索動作を再度確認し、ステージングと同じ結果が得られることを確認して公開としました。
実装上の注意点と補足
インデックスの自動更新
投稿の作成・更新・削除時にはRelevanssiが自動でインデックスを差分更新します。save_postフックで処理されるため、カスタムフィールドの値が変更された場合も自動的に反映されます。定期的な手動再インデックスは通常不要です。
パフォーマンスwp_relevanssiテーブルはインデックスサイズに応じて肥大化します。今回の数千件規模では問題が出ませんでしたが、数万件を超える場合はクエリのレイテンシとDBサーバーの負荷を事前に見積もっておくべきです。Relevanssiには検索結果のキャッシュ機能(relevanssi_cacheテーブル)もあるため、トラフィックが多い環境では有効化を検討する必要があります。
Relevanssi Premiumが必要になるケース
以下の要件が発生した場合は有料版への移行を検討する必要があります。
- 添付ファイル(PDF・Wordなど)の内容を検索対象にしたい
- ユーザーロールや権限に応じて検索結果を出し分けたい
- WooCommerceの商品バリエーションを個別にインデックスしたい
まとめ
今回の対応は、WordPressのデフォルト検索ではカバーできないACFカスタムフィールドへの検索要件をRelevanssiで解決したケースでした。実装のポイントを整理すると以下になります。
- 大量投稿サイトでのインデックス構築はWP-CLIを使う
- カスタムフィールドは
all fieldsではなくフィールド名を個別指定してノイズを排除する - 日本語の複雑なクエリが要件に含まれる場合はRelevanssiだけでは限界があり、別のアーキテクチャを検討する
- スコアの重み付けはリリース後もチューニングする前提で設計する
the_excerpt()をrelevanssi_the_excerpt()に差し替えることでスニペット品質が大幅に向上する
プラグインの導入自体は低コストだが、インデックス設計と重み付けのチューニングで品質が大きく変わります。検索要件が明確なうちにフィールド指定とスコアリングの方針を固めておくことが、後から手戻りを減らすうえで重要です。

コメントを残す