🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

1回答

2316閲覧

WordPressの「meta_query」指定において、「compare」を「REGEXP」にすると、「ais;^.」の6文字でバグがでる

amegahutteruyo

総合スコア15

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2019/10/09 11:40

編集2019/10/09 11:47

###問題の概要
不思議な現象を見つけました。

WordPressでREGEXP検索を使うと、下記【特定の文字】を入力した際に該当しない記事を取得してきてしまいます。

【特定の文字】a,i,s,;,^,.

これらをもっていない記事がなぜか表示されるのです。

###問題が生じる状況

WordPressでお馴染みの$the_query = new WP_Query( $args );で、$argsに次のようにmeta_queryを指定しています。

php

1$args = []; 2$args['post_type'] = 'testpost'; 3$words_arr = get_meta_query_search( $search_str, 'or' ); 4$args['meta_query'] = array(array( 5 array( 6 'key' => 'search_fields', 7 'value' => convert_arr_to_regex( $word_arr, 'or' ), 8 'compare' => 'REGEXP', 9 ) 10)); 11$the_query = new WP_Query( $args );

各関数は後述しますが、$search_strりんご ばななであったとき、りんご|ばななsearch_fields検索するという流れです。

このりんご ばななでの検索はできています。

不思議な現象というのは、この$search_strが【特定の文字】であったとき、search_fieldsにその6文字が存在しない記事を取得してきてしまうという現象です。

###試したこと
次のようにREGEXP=にしました。
そしてvalueを【特定の文字】であるaにしても、search_fieldsaがなければ記事を取得するという現象は起こりませんでした。

php

1$args = []; 2$args['post_type'] = 'testpost'; 3$args['meta_query'] = array(array( 4 array( 5 'key' => 'search_fields', 6 'value' => 'a', 7 'compare' => '=', 8 ) 9)); 10$the_query = new WP_Query( $args );

###質問
この現象の問題点と解決策を知りたいです。

なお目的は、カスタムフィールドsearch_fieldsに、$search_strを含む記事の検索機能になります。後述の関数を使う流れは変更したくないです。

ご指導宜しくお願い致します。

###各関数について
ここまでで書いた各関数について記載させていただきます。

/* WP_Query の $args に meta_query での条件を追加します カスタムフィールド search_fields に $search_str を含む記事を取得します */ function get_meta_query_search( $search_str, $join ){ $search_arr = my_get_search_arr_from_str( $search_str ); $meta_query = array( 'key' => 'search_fields', 'value' => convert_arr_to_regex( $search_arr, $join ), 'compare' => 'REGEXP', ); return $meta_query; } /* 検索に使うため、$search_str を配列に分解します 先頭のシャープを削除し、検索に便利なよう全角半角変換します */ function my_get_search_arr_from_str( $search_str ){ // 入力値を配列にする $search_str = mb_convert_kana($search_str, 's'); $search_arr = preg_split("/[\s]+/", $search_str ); // 先頭がシャープの場合は1つだけ削除して配列へ $word_arr = []; $term_arr = []; foreach( $search_arr as $search ){ $search = preg_replace("/( | )/", "", $search ); if( mb_substr($search, 0, 1) === '#' || mb_substr($search, 0, 1) === '#' ){ $search = mb_substr( $search , 1 , strlen($search)-1 ); if( $search !='' ){ $word_arr[] = $search; } } else{ if( $search !='' ){ $word_arr[] = $search; } } } // 変換した配列を返す $word_arr_convert = []; foreach( $word_arr as $word ){ $word_arr_convert[] = mb_convert_kana($word, 'kvrn'); $word_arr_convert[] = mb_convert_kana($word, 'KVRN'); } return $word_arr_convert; } /* meta_query 検索で REGEXP を使うため $arr を次のように変換します ['りんご','ばなな'] → 'りんご|ばなな' $type が 'or' なら OR検索で使うという感じです */ function convert_arr_to_regex( $arr, $type ){ if($type=="or"){ $result = implode('|',$arr); }elseif($type=="and"){ $result = []; foreach( $arr as $ar ){ $result[] = '(?=.*' . $ar . ')'; } $result = '^' . implode('',$result); } if(empty($arr)){ $result = ''; } return $result; }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

^や.は正規表現で意味のある文字、\でエスケープしないとその文字を検索対象にはできない

a,i,sが引っかかるって言うのはもしかして
検索対象のカスタムフィールドには
配列をシリアライズした値が入ってるんじゃないんですか?

投稿2019/10/09 23:54

KazuhiroHatano

総合スコア7819

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

amegahutteruyo

2019/10/10 06:09 編集

たしかにシリアライズされた配列で使われる文字列ですね。なるほど。 カスタムフィールドは次のように更新しているので配列は入っています。 update_post_meta( $pst_id, 'search_fields', ['りんご','ばなな'] ); そして調べたところWordPressは上の処理で自動的にシリアライズをかけるそうでした。 しかしそうなると困るのが、それらを正しく認識する方法です。 つまりDBにあるのは ['りんご','ばなな'] でなく "a:2:{i:0;s:9:"りんご";i:1;s:9:"ばなな";}" ですが、 それをまず先に ['りんご','ばなな'] に戻してから検索をかけないといけませんよね? しかしそのようはことはWP_Query内でできない(できても無駄な処理が増えるだけ?)と思うのですが、いかがでしょうか? ということは、もしかしたらカスタムフィールドに配列をいれたとき、質問にあるようなREGEXPを使うのはお門違いということなのでしょうか?
KazuhiroHatano

2019/10/10 06:45

検索対象になりうるフィールドは配列にしない方がいいです 単語毎に一致するものを検索するならば検索文字列を シリアライズしてから検索するという方法がとれますが (eg: りんご → LIKE '%s:9:"りんご"%' で検索) 単語内の部分一致を含めたいならこの方法はとれません いずれにせよAND検索の方のSQLがよろしくない (単語の出現の順番が違うだけで検索にかからない)ので もう少し処理の方法を考える必要があるかと思います
amegahutteruyo

2019/10/10 08:27

>単語の出現の順番が違うだけで検索にかからない 初めてだったので今知れてよかったです。ありがとうございます。 >検索対象になりうるフィールドは配列にしない方がいいです 例えば貴方なら、['りんご','ばなな'] をどのようにupdate_post_metaで入れますか?以下 $your_ideas のステップを示していただけないでしょうか。 $array = [ 'りんご', 'ばなな' ]; $your_ideas = ; update_post_meta( $post_id, 'search_fields', $your_ideas ); それとも「>処理の方法を考える」とは、この保存のステップでなく別の話でしょうか?
KazuhiroHatano

2019/10/10 08:38

delete_post_meta()してforeachで一個ずつadd_post_meta() これで本来WPが想定している値一つにつき一行というカスタムフィールドの形になります meta_queryも本来はこの形でデータベースに値が入っていることを想定しています しかしこのWP自体が想定している方法はDBの行数の増え方が激しく SQLの処理の重さの増加も大きいので 配列をシリアライズしていれるようにしてるプラグインが多いです 記事数が1000以上になるようなサイトならシリアライズする形のままでやって 単語内の部分検索については諦めるか妥協する方がいいかもしれません
KazuhiroHatano

2019/10/10 08:43

そういえばシリアライズじゃなく、json_encodeして入れてるのもいました、もしかしたらこっちならわりといけるかもしれないですね
KazuhiroHatano

2019/10/10 08:44

ただ値の取り出しの時にjson_decodeしないといけないのが面倒
amegahutteruyo

2019/10/10 09:11

なるほど、値一つずつですか。しかしjson_encodeならシリアライズのような余計な文字が追加されずいいかもしれませんね。その方向で進めていこうと思います。また機会がございましたらアドバイスいただければ幸いです。どうもありがとうございました。
amegahutteruyo

2019/10/10 12:19

これ思ったのですが、なぜそもそもシリアライズなんでしょうね?文字数も増えますし、今回のような問題もありますし、json_encodeの方がいいのでは?何か問題ありますか?
KazuhiroHatano

2019/10/10 12:28

値にクラスのオブジェクトを入れる場合だってあるでしょうからね、jsonじゃ型を保持できないです スカラー型でも文字列型に統一されちゃったりするので油断ならないです
amegahutteruyo

2019/10/10 12:46

たしかに、いろいろとありがとうございます。一日かけてなんとか次のような流れのjson_encode版で実装できました。 my_update_post_meta_encode( $post_id, $key, stripslashes($_POST[$key]) ); function my_update_post_meta_encode( $id, $key, $val ){ $encoded = json_encode( $val, JSON_UNESCAPED_UNICODE ); update_post_meta( $id, $key, $encoded ); } 最後に質問の記事検索機能になるのですが、上の流れで登録したとして、検索のmeta_queryはどのように書きますか?質問の「問題が生じる状況」のコードで「join 」を「and」にしても検索でひっかかりませんでして…
KazuhiroHatano

2019/10/10 13:41

一回の正規表現検索で複数の文字列を順不同でAND検索するのは無理です meta_queryの項目を分けるしかないです
amegahutteruyo

2019/10/10 16:00 編集

すみません「項目をわける」とは?
amegahutteruyo

2019/10/10 15:59

あ失礼しました。「りんご ばなな」のときは、「'relation' => 'AND'」でそれぞれつなぐわけですよね?そこは知っていました。 ただ「りんご ばなな きゅうり」だったり「りんご」だけだったりなど、その組み合わせ方が変化しうる中では、どのように書きますか? もし具体的なコードで示すのがご面倒でなければお願いしたいです。
KazuhiroHatano

2019/10/11 01:54

array_mapなりforeachなりで単語毎に['compare'=>LIKE,'value'=>'%単語%'....]の配列を作ってmeta_queryに入れりゃいいんじゃないですかね
amegahutteruyo

2019/10/11 04:22

なるほど、できそうです。何度もすみません。どうもありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問