【WordPress】カスタムフィールドを検索対象にする方法|posts_joinで正しく実装・プラグインも

【WordPress】カスタムフィールドを検索対象にする方法 WordPress

WordPressの標準検索は投稿のタイトルと本文だけが対象で、カスタムフィールド(メタ)の値は検索されません。カスタムフィールドに保存した情報も検索でヒットさせるには、検索クエリのSQL自体に手を入れる必要があります。この記事では、その正しい実装方法と、よくある間違いを解説します。

この記事でわかること

  • なぜ meta_query ではカスタムフィールドを検索対象にできないのか
  • posts_joinposts_whereposts_distinct による正しい実装
  • Relevanssi などプラグインを使う方法
  • 性能・全メタキー検索などの注意点
スポンサーリンク

なぜ meta_query では検索対象にできないのか

よくある間違いが、pre_get_postsmeta_query を設定する方法です。これはうまくいきません。検索語(s)がある状態で meta_query を足すと、WordPressは次のような AND 条件のSQLを作るためです。

meta_queryを足すと生成されるSQL(イメージ)
WHERE (post_title LIKE 'X' OR post_content LIKE 'X')   -- 標準検索
  AND (meta_1 LIKE 'X' OR meta_2 LIKE 'X')             -- meta_query は AND で結合
AND結合で逆効果になる
上のSQLは「タイトル/本文 かつ メタの両方に一致」する投稿しか返しません。キーワードがカスタムフィールドだけにある投稿は見つからず、さらに本来タイトルでヒットしていた投稿も、メタに同じ語が無ければ消えてしまいます。つまり目的(メタも検索対象に=OR)とは逆の挙動になります。メタ値を「OR」で検索に加えるには、次のSQLレベルの実装が必要です。

方法1:プラグインを使う(手軽で堅牢)

もっとも手軽で安全なのは専用プラグインです。Relevanssi は標準検索を全文検索に置き換え、カスタムフィールド・タクソノミー・コメントまで検索対象に追加でき、関連度順の並び替えにも対応します。ほかに SearchWP(有料・高機能)、WP Extended Search(GUI設定)などがあります。コードを書かずに管理画面から設定でき、性能面の最適化も任せられるのが利点です。

方法2:posts_join / posts_where / posts_distinct で実装する

プラグインを使わずに実装する場合は、検索時に wp_postmeta を JOIN し、WHERE 句にメタ値の OR 条件を加え、JOINで生じる重複行を DISTINCT で除きます。次の3つのフィルターをテーマ(できれば子テーマ)の functions.php に追加します。

functions.php:検索時に postmeta を JOIN する
function cf_search_join($join, $query) {
  global $wpdb;
  if (!is_admin() && $query->is_main_query() && $query->is_search()) {
    $join .= " LEFT JOIN {$wpdb->postmeta} AS cf_search ON {$wpdb->posts}.ID = cf_search.post_id ";
  }
  return $join;
}
add_filter('posts_join', 'cf_search_join', 10, 2);

次に、既存の「タイトルにLIKE」条件へ、メタ値の OR を追記します。検索対象にしたい meta_keyIN (...) で限定すると、不要な内部メタ(_ で始まる非公開キーなど)を検索しなくて済みます。

functions.php:WHERE にメタ値の OR を追加する
function cf_search_where($where, $query) {
  global $wpdb;
  if (!is_admin() && $query->is_main_query() && $query->is_search()) {
    $where = preg_replace(
      "/\(\s*{$wpdb->posts}.post_title\s+LIKE\s*('[^']+')\s*\)/",
      "({$wpdb->posts}.post_title LIKE $1) OR (cf_search.meta_key IN ('custom_field_1', 'custom_field_2') AND cf_search.meta_value LIKE $1)",
      $where
    );
  }
  return $where;
}
add_filter('posts_where', 'cf_search_where', 10, 2);

最後に、JOINで同じ投稿が複数行になるのを防ぐため DISTINCT を付けます。

functions.php:重複行を DISTINCT で除く
function cf_search_distinct($distinct, $query) {
  if (!is_admin() && $query->is_main_query() && $query->is_search()) {
    return 'DISTINCT';
  }
  return $distinct;
}
add_filter('posts_distinct', 'cf_search_distinct', 10, 2);
実装時の注意
検索する meta_key は限定する(全メタを対象にすると非公開キーやシリアライズ値まで拾い、性能も低下)
・カスタムフィールドが多い・記事数が多いサイトでは検索が重くなることがあります。その場合はプラグインやクエリ最適化(meta_query・tax_queryの使い分け)も検討してください
functions.php の編集前にバックアップを取りましょう

検索カスタマイズの関連記事

よくある質問(FAQ)

Q標準検索でカスタムフィールドが検索されないのはなぜですか?
AWordPressのデフォルトの検索クエリは post_title(タイトル)と post_content(本文)だけを対象にしているためです。カスタムフィールド(wp_postmeta)は別テーブルにあり、検索するにはJOINとWHEREの追加が必要です。
Qmeta_query を設定すれば検索対象になりますか?
Aなりません。検索語がある状態で meta_query を足すと、標準検索の条件と AND で結合され、「タイトル/本文 かつ メタの両方に一致」する投稿しか返らなくなります。メタをORで加えるには posts_join / posts_where / posts_distinct を使うか、プラグインを利用します。
QRelevanssi はどんなプラグインですか?
A標準のLIKE検索を全文検索に置き換えるプラグインです。カスタムフィールド・タクソノミー・コメントを検索対象に追加でき、関連度順の並び替えや検索語のハイライトにも対応します。無料版でも基本機能が使えます。

まとめ

  • 標準検索はタイトル・本文のみ。メタは対象外
  • meta_query は不可:検索語とANDになり逆効果
  • 正しい実装posts_joinposts_where(OR追加)+ posts_distinct
  • 手軽さ・堅牢さ重視なら Relevanssi などプラグイン
  • 注意:meta_key を限定し、性能とバックアップに配慮

「メタも検索したい」は OR で加えるのが正解です。要件に応じてコードかプラグインを選び、検索対象のキーは絞って実装しましょう。