サポート » 使い方全般 » meta_queryのvalue値に渡す引数について

  • お世話になっております。

    チェックボックスの動的な値を取得し、
    それをmeta_queryを使用して検索しようとしておりますが、
    下記コードでは検索がヒットしません。

    $keywd = "array('tokyo','kanagawa')";
    
    $args = array(
        'meta_query' => array(
          'relation' => 'OR',
          array(
            'key' => 'area',
            'value' => $keywd ,
            'compare' => 'IN'
          )
        )
    );

    下記の様に直書きだとうまくいきますが、引数を使うとヒットしません。
    8行目 ‘value’ => array(‘tokyo’,’kanagawa’),

    meta_queryのvalueにarrayの引数を渡すことはできませんか?
    何か良い方法はないでしょうか。
    どうぞ宜しくお願い致します。

15件の返信を表示中 - 1 - 15件目 (全17件中)
  • $keywd の内容を”で囲んでいるせいではないでしょうか?
    $keywd = array(‘tokyo’,’kanagawa’);
    にしてみてください。

    トピック投稿者 kaiji

    (@kaiji)

    ありがとうございます。
    できました!

    今tokyo,kanagawaにしているところも引数にしてできました。
    先に進めそうです。

    $keywd = array($arr_chk[0],$arr_chk[1]);

    出来ないときは下記のようにして悩んでいました。
    $keywd = “array(‘” . $arr_chk[0] . “‘,'” . $arr_chk[1] . “‘)”;

    ありがとうございました。

    トピック投稿者 kaiji

    (@kaiji)

    areaというkeyはラジオボタンで追加された項目であったのためヒットしたのですが、
    チェックボックスの項目(service)に対してはヒットしませんでした。

    チェックボックスの場合格納されているデータがラジオボタンとは違うことがわかりました。
    array(2) { [0]=> string(18) "ショッピング" [1]=> string(12) "サービス" }

    compareをLIKEにして検索するようにしました。

    $args = array(
        'meta_query' => array(
          'relation' => 'OR',
          array(
            'key' => 'service',
            'value' => $arr_gnr[0],
            'compare' => 'LIKE'
          ),
          array(
            'key' => 'service',
            'value' => $arr_gnr[1],
            'compare' => 'LIKE'
          ),
          array(
            'key' => 'service',
            'value' => $arr_gnr[2],
            'compare' => 'LIKE'
          )
        )
    );
    }
    $user_query = new WP_User_Query( $args );

    しかし
    サーバーからアクセス制限がかかってしまうほど検索時間がかかってしまい実用できません。

    チェックボックスは25項目程あり、現状2つのLIKE検索でいっぱいいっぱいです。
    compare=>INで検索できる良い方法はありませんでしょうか

    配列の値一つ一つにLIKEではなく

    array(
            'key' => 'service',
            'value' => $arr_gnr,
            'compare' => 'LIKE'
          )

    ではできませんか?

    トピック投稿者 kaiji

    (@kaiji)

    gogowebさん、ありがとうございます。

    仰る通り試してみましたが、ヒットしませんでした。

    下記の、ラジオボタン項目の既にヒットしている条件で、
    INをLIKEにかえて試してみましたが、ヒットしなくなりました。
    (INだとヒットします。)

    $keywd = array('tokyo','kanagawa');
    $args = array(
        'meta_query' => array(
          'relation' => 'OR',
          array(
            'key' => 'area',
            'value' => $keywd ,
            'compare' => 'IN'
          )
        )
    );

    value値をarrayで指定した場合、LIKEでの検索がヒットしなくなりました。

    カスタムフィールドが配列なのではなく検索候補が複数なのですね。
    その場合はLIKEではなくINを使います。
    それこそ上の例でareaがtokyoまたはkanagawaを含んでいる場合にマッチすると思うのですが。

    トピック投稿者 kaiji

    (@kaiji)

    説明が足りず申し訳ございません。

    上の例は、
    カスタムフィールドが配列ではなく、
    複数の検索候補もマッチさせることのできている記述を
    仮にLIKE検索にした場合を試してみました。

    結果ヒットしなくなったため、
    LIKE検索でのvalue値にarrayが使えないと推測できました。

    求めているのは
    カスタムフィールドが配列で、検索候補も複数でマッチさせることです。

    上記の推測からLIKE検索ではvalue値に$arr_gnr[0]のように
    ひとつひとつの検索要素をあてがう必要があると考えましたが、
    検索結果を吐き出すのに時間がかかりすぎてしまうことが問題です。

    サーバーのスペックが問題となるでしょうか。
    もし記述的に回避できるのであればご教授頂きたいです。

    どうか宜しくお願い致します。

    カスタムフィールドの全文検索は相当時間がかかるので、
    カスタムタクソノミー(カスタム分類)を使用すると、データベースにインデックスが設定されているため、そこそこ高速に動作するようになると思います。

    エリアやジャンルなどは、タクソノミーにしやすいものだと思います。

    トピック投稿者 kaiji

    (@kaiji)

    Toro_Unitさん
    返信が遅くなってしまってしまい申し訳ございません。

    貴重なアドバイスありがとうございます。
    やってみます

    ご報告致します。

    カスタムタクソノミーは、post データの分類に使うので、wp_users と wp_usermeta テーブルを相手にするWP_User_Query() では検索できなくなってしまうのではないでしょうか? それから、meta_key には一応、ユニークではないインデックスがはられています。ただし、LIKE 検索で、データの先頭にワイルドカードがあると、インデックスが使われなくなってしまうのです(WordPress は前後にワイルドカードを付け加えますが、ユーザがコントロールできないようになっています)。

    kaijiさん、よくわからないところがあるので、ひとつ実験してみてもらえませんか? 時間がかかって、タイムアウトするmeta query を下のようにして、実際の SQL ステートメント変換してチェックすることができます。

    $args = array(
        kaijiさんが使った引数
        );
    $meta_query = new WP_Meta_Query();
    $meta_query->parse_query_vars($args);
    $query_string = $meta_query->get_sql('user', $wpdb->users, 'ID', null);
    print_r($query_string);

    インスタンスメソッドを使って、SQL 文を作るだけなので、WP_Meta_Query() には引数を入れません。もちろん、実行もされません。配列データが返るので、print_r() か var_dump() を使って表示させて、結果をポストしてみてください。それで解決する保証はありませんが…

    これから20時間ほど unplugged になるので、すぐには返信できないかもしれませんが、もしよろしければ、やってみてください。

    トピック投稿者 kaiji

    (@kaiji)

    kjmtshさん
    ありがとうございます。
    勉強になります。

    やはりカスタムタクソノミーはユーザーの範囲外なんですね。

    頂いたソースを実行したものが以下になります。

    Array ( [join] => INNER JOIN xxx_usermeta ON (xxx_users.ID = xxx_usermeta.user_id) INNER JOIN xxx_usermeta AS mt1 ON (xxx_users.ID = mt1.user_id) [where] => AND ( (xxx_usermeta.meta_key = 'genre' AND CAST(xxx_usermeta.meta_value AS CHAR) LIKE '%men%') OR (mt1.meta_key = 'genre' AND CAST(mt1.meta_value AS CHAR) LIKE '%cafe%') ) )

    返信が遅くなってしまい申し訳ございません。
    宜しくお願い致します。

    うーん、タイムアウトするほどのクエリには見えないのですが… 考えられる原因は、やはり、INNER JOIN と LIKE なのですが、それにしても遅すぎますよねぇ…? 試しにコマンドラインで同様のクエリを実行してみたら、0.01ミリ秒で終わりました。ひょっとして、ユーザデータは10000件を越えてますか?

    SQL をぱっと見た感じでは、同じ文が複数あって、無駄に見えるかもしれませんが、不特定の要求に対して、絶対安全確実な SQL 文を作るという点では、WordPress の SQL 文生成はよくできていて、私の知る限り、冗長になることはあっても、間違った文を作ったことは1度もありません。

    それでも、JOIN と LIKE が遅いことは確かです。JOIN はユーザが変更できませんから、LIKE を何とかしましょう、ということになります。LIKE は、前に述べたことから、どうしても使わなければならないとき以外は、極力使わないようにした方がよいと思います。

    key LIKE '%data%'

    の % は、ワイルドカードなので、data を含む文字列は全て検索にヒットします。これは、保存されているデータがユーザの入力によるもので、データにブレがある、たとえば、「東京」と「東京都」が混在している場合、’東京%’ と指定すると、「東京」で始まるデータを全て抽出できるのですね。

    この場合、LIKE 検索を使うよりも、入力の段階でリストから選ぶようにするなどの対策をしておいて、’=’ 検索を実行した方が遙かに高速動作します。

    kaiji さんの場合、問題の一つは、上のように LIKE を使わなければならないかどうか、という点にあります。これがどうしても必要ということになると、残念ながら他に手段はありません。自前で SQL 文を作る(たとえば、INNER JOIN を使わない)ということも考えられますが、LIKE を最大 25 回反復するのはどうにもなりません。

    そうではなくて、’=’ 検索、あるいは、’IN’ でもよい、つまり、データに曖昧さがない、ということになれば、meta query を作る前の段階に処理を追加することで、スピードアップできるかもしれません。

    まず、条件ですが、25個のチェックボックスから飛んでくるデータが、同じ meta_key に属するものか、複数の meta_key が存在するのかが問題になります。毎回の投稿で、meta_key の値が違うので、これまでの投稿からは推測できないのですが、前者なら、

    array( 'men', 'women', 'cafe', 'restaurant', ...)

    みたいな1次元配列にまとめます。この場合、チェックボックスから飛んでくるデータがすでに配列になっているはずなので、あまり手間はかからないと思います。

    後者の場合、meta_key を配列として指定することはできません。データの方を、meta_key に合わせる形で整理します。具体的には、

    meta_key1 のデータ => array( ‘men’, ‘women’, etc…)
    meta_key2 のデータ => array( ‘cafe’, ‘restaurant’, etc…)

    というように、複数の配列に分割するわけです。

    さて、実際の引数処理ですが、前者の場合は、こうなります。なお、’IN’ は一つずつ ‘=’ と ‘OR’ で検索するのと同じ意味を持ちますから、どれか一つでもヒットすれば、そのユーザのデータが返ります。

    $args = array(
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key'     => 'meta_key',
                'value'   => array('men', 'women', etc...),// チェックボックスのデータ
                'compare' => 'IN'
            ),
            その他...
        )
    );

    後者の場合。

    $args = array(
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key'     => 'meta_key1',
                'value'   => array('men', 'women'),// チェックボックスのデータ1
                'compare' => 'IN'
            ),
            array(
                'key'     => 'meta_key2',
                'value'   => array('cafe', 'restaurant'),// チェックボックスのデータ2
                'compare' => 'IN'
            ),
            その他...
        )
    );

    meta_key の数だけ配列を追加していけばいいわけです。配列が空でも動作はしますが、その場合の処理もあったほうがよいかもしれません。

    こんな説明でわかりますか? あんまりうまく説明できていないような気がして、自信がありませんが…

    トピック投稿者 kaiji

    (@kaiji)

    kjmtshさん
    分かりやすくご説明頂き、ありがとうございます。
    よく理解できました。

    ユーザーデータは100件程です。(将来的にもmax500程度)
    私もこのボリュームでこんなに時間がかかるものかと疑問をもっています。

    meta_keyは同じ属性です。
    しかし頂いた記述で試してみましたがヒットさせることができませんでした。

    データベースに格納されているデータ例は、こちら↓です。
    a:2:{i:0;s:7:”gourmet”;i:1;s:4:”cafe”;}

    kjmtshさんが仰るようにデータに曖昧さがあるためですよね?

    データと照合する際に、キーワードのみを抽出して照合できますか?
    a:2:{i:0;s:7:”gourmet”;i:1;s:4:”cafe”;} -> gourmet , cafe

    それとも
    a:2:{i:0;s:7:”gourmet”;i:1;s:4:”cafe”;}と格納するのではなく
    gourmet , cafeと格納する方法を考えた方が良いのでしょうか?

    見当違いの質問でしたらすみません。
    宜しくお願い致します。

    おおっと、シリアライズされていたのかぁ… このデータを最初に出していれば、簡単な話しでしたねぇ。

    結論は、「meta query を使うことができない」です。無理をすれば、’LIKE’ が使える、と付け加えればもっとよい答えになるかもしれません。MySQL はこれが文字列だと思って比較してますから、’=’ も ‘IN’ も使えないのです。WordPress 側で serialize してますから、WordPress が責任をもって unserialize しないといけません。

    meta query で ‘=’ や ‘IN’ が使えるデータは、例えば下のようになります。meta_key のインデックスは重複を許すものになっているので、こうできるのです。これなら、meta query が普通に使えます。

    +---------+----------+------------+
    | user_id | meta_key | meta_value |
    +---------+----------+------------+
    | 1       | area     | tokyo      |
    +---------+----------+------------+
    | 2       | area     | kanagawa   |
    +---------+----------+------------+
    | 1       | genre   | cafe       |
    +---------+----------+------------+
    | 2       | genre   | gourmet    |
    +---------+----------+------------+

    現状のままで何とかするには、予備のクエリを実行して、シリアライズされたデータをチェックボックスから飛んできたデータと比較するしかないと思います。たとえば、あくまでたとえばですが、下のような操作をしておいて、WP_User_Query インスタンスを作るということは考えられるかもしれません。

    global $wpdb;
    $meta_key = 'メタキーの値を入れてください';
    $strings_to_search = チェックボックスから得た1次元配列データ;
    
    $results = $wpdb->get_results($wpdb->prepare("SELECT user_id, meta_value FROM $wpdb->usermeta WHERE meta_key='%s'", $meta_key));
    
    foreach ($results as $result) {
        $user_meta_values = maybe_unserialize($result->meta_value);
        if (!is_array($user_meta_values)) continue;
        foreach ($user_meta_values as $value) {
            if (in_array($value, $strings_to_search)) {
                $users_to_search[] = $result->user_id;
                break;
            }
        }
    }
    
    $args = array(
        'ID' => $users_to_search
    );
    
    $user_query = new WP_User_Query($args);

    チェックボックスのデータは ‘OR’、他の条件は ‘AND’ の検索するのと同じ結果になると思いますが… 自分では試してません。

    トピック投稿者 kaiji

    (@kaiji)

    kjmtshさん
    返信が遅くなってすみません。

    出来ました!

    global $wpdb;
    $meta_key = 'genre';
    $strings_to_search = $arr_gnr;
    
    $results = $wpdb->get_results($wpdb->prepare("SELECT user_id, meta_value FROM $wpdb->usermeta WHERE meta_key='%s'", $meta_key));
    
    foreach ($results as $result) {
        $user_meta_values = maybe_unserialize($result->meta_value);
        if (!is_array($user_meta_values)) continue;
        foreach ($user_meta_values as $value) {
            if (in_array($value, $strings_to_search)) {
                $users_to_search[] = $result->user_id;
                break;
            }
        }
    }
    
    $args = array('include' => $users_to_search);
    $user_query = new WP_User_Query( $args );

    検索結果はサクッと表示されました。

    知識が浅く質問に必要な情報もお伝えすることができず、
    ご迷惑をお掛けしましたが、皆様の適格なアドバイスに感謝しております。
    今後ともどうぞ宜しくお願い致します。

    ありがとうございました。

15件の返信を表示中 - 1 - 15件目 (全17件中)
  • トピック「meta_queryのvalue値に渡す引数について」には新たに返信することはできません。