web-dev-qa-db-ja.com

追加の$ sentence値で検索クエリを拡張する

WordPressを拡張するという私の探求の次は、$sentence変数が特定の基準を満たすときに、WordPressがposts_search$sentence変数を使用して追加の文字列を含める方法を変更することです。

ここでの特定のユースケースは、誰かがTL123のような何かをタイプするとき、TL-123も検索するべきであるということです、ここでTL%はここでのワイルドカードです。これは検索ミスを減らすためです(ハイフンを含まないもの)。

追加のSQLクエリを使ってposts_searchにフィルタする方法(これまでは実際に this one を使っています)を見たことがありますが、これがどのように機能するかについては少しあいまいです。任意の助けは大歓迎です。ありがとうございます。

編集 - @kaiserのおかげでニーズに関するより多くの情報を含めることができます

  • 実際の投稿のタイトルはほとんどの場合TL-123であるため、検索は通常TL123と入力して行われます。したがって、ここでの目標は傍受することです。検索クエリにTL(NUMBER)が含まれ、さらにTL - (NUMBER)も検索される場合。

繰り返しますが、ここでのトリックは、一部の投稿にはTLではなくTLのみが含まれているため、追加の「部分フレーズ」も検索するためにそのパターンに一致する検索クエリを探している理由です。

ありがとうございます。

更新

それでは、kaiserのスターター機能を基にして、次のことを思いつきました。

function wpse66815_search_query_string( $search, &$wp_query )
{
    if (!is_admin() && is_search()) {

        print_r($search);

        global $wp_query;

        // get search term
        $search_term = array_shift($wp_query->query_vars['search_terms']);
        // specify string we'll use to replace
        $replace_var = 'TL';

        // find matches for that string
        preg_match_all("/{$replace_var}(?:[^0-9]*)(\d+)/i", $search_term, $out);

        // if there's no matches, return the normal search
        if ( empty($out[0]) )
            return $search;

        // find/generate the search term with the replacement
        $modified_search_term = preg_replace("/{$replace_var}(?:[^0-9]*)(\d+)/i", "{$replace_var}-$1", $search_term);

        // combine both the regular and modified search term
        $new_search[] = $search_term;
        $new_search[] = $modified_search_term;

        //var_dump($new_search);

        // generate the new search query
        foreach ( $new_search as $keyword )
        {
            $new_string_parts[] = $GLOBALS['wpdb']->prepare(
                 "
                    AND ((%s.post_title LIKE '%%%s%%') OR (%s.post_content LIKE '%%%s%%'))
                 "
                ,"{$GLOBALS['wpdb']->prefix}posts"
                ,like_escape( $keyword )
                ,"{$GLOBALS['wpdb']->prefix}posts"
                ,like_escape( $keyword )
            );
        }

        // set $search equal to results
        $search = implode( " ", $new_string_parts );

        //print_r($search);
    }

    return $search;
}
add_filter('posts_search', 'wpse66815_search_query_string',500,2);

私が乗り越えることができない部分は、実際には実際のSQLクエリです - >下記のエラーです。

WordPress database error: [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.post_title LIKE 'tl123') OR ('wp__posts'.post_content LIKE 'tl123')) ' at line 2]

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (('wp__posts'.post_title LIKE 'tl123') OR ('wp__posts'.post_content LIKE 'tl123')) AND (('wp__posts'.post_title LIKE 'TL-123') OR ('wp__posts'.post_content LIKE 'TL-123')) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'galleries', 'idea_gallery', 'moulding_profiles', 'moulding_collection', 'moulding_combination') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 2 AND wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 80

どこに問題があったのかについての質問はありますか。ありがとうございます。

更新#2

私はいくつかの問題を見始めました:

  • それはwp__postsの代わりにwp_postsをしていました(私はそれを上で更新しました)
  • kaiserが述べたように、LIKE %s部分はLIKE %%s%である必要があるかもしれませんが、それは正しく構文解析されていません。

エラーは今です:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (('wp_posts'.post_title LIKE 'tl123') OR ('wp_posts'.post_content LIKE 'tl123')) AND (('wp_posts'.post_title LIKE 'TL-123') OR ('wp_posts'.post_content LIKE 'TL-123')) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'galleries', 'idea_gallery', 'moulding_profiles', 'moulding_collection', 'moulding_combination') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 2 AND wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 80

上のエラーは(私が言うことができるものから)実際にはtl123TL-123の両方である投稿を探すことですが、私はORが私が欲しいものであると思います(どちらかの状況で投稿を返したいので)。

更新#3

%sを正しくエスケープするように関数を更新しました。エラーは次のようになりました。

WordPress database error: [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.post_title LIKE '%tl123%') OR ('wp_posts'.post_content LIKE '%tl123%')) ' at line 2]

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (('wp_posts'.post_title LIKE '%tl123%') OR ('wp_posts'.post_content LIKE '%tl123%')) AND (('wp_posts'.post_title LIKE '%TL-123%') OR ('wp_posts'.post_content LIKE '%TL-123%')) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'galleries', 'idea_gallery', 'moulding_profiles', 'moulding_collection', 'moulding_combination') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 2 AND wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 80

更新#4

これが私がやることになったものです - > foreachは(@kaiserが述べたようにAND/OR演算子で)正しくラッピングしていなかったので、私はそれらをそれら自身の配列挿入に分けました。

function wpse66815_search_query_string( $search, &$wp_query )
{
    if (!is_admin() && is_search()) {

        //print_r($search);

        global $wp_query,$wpdb;

        // get search term
        $search_term = array_shift($wp_query->query_vars['search_terms']);
        // specify string we'll use to replace
        $replace_var = 'TL';

        // find matches for that string
        preg_match_all("/{$replace_var}(?:[^0-9]*)(\d+)/i", $search_term, $out);

        // if there's no matches, return the normal search
        if ( empty($out[0]) )
            return $search;

        // find/generate the search term with the replacement
        $modified_search_term = preg_replace("/{$replace_var}(?:[^0-9]*)(\d+)/i", "{$replace_var}-$1", $search_term);

        // combine both the regular and modified search term
        $new_search[] = $search_term;
        $new_search[] = $modified_search_term;

        var_dump($new_search);

        // generate the new search query
        $new_string_parts[] = $wpdb->prepare( "AND ((({$wpdb->posts}.post_title LIKE '%%%s%%') OR ({$wpdb->posts}.post_content LIKE '%%%s%%'))",like_escape( $new_search[0] ),like_escape( $new_search[0] ));
        $new_string_parts[] = $wpdb->prepare( "OR (({$wpdb->posts}.post_title LIKE '%%%s%%') OR ({$wpdb->posts}.post_content LIKE '%%%s%%')))",like_escape( $new_search[1] ),like_escape( $new_search[1] ));

        // set $search equal to results
        $search = implode( " ", $new_string_parts );

        //print_r($search);
    }

    return $search;
}
add_filter('posts_search', 'wpse66815_search_query_string',500,2);

誰かがtl123 tl456tlキーワードが2回出現する)を入力しようとした場合、それはいくつかの問題を抱えていますが、私はその部分に取り組んでいます。ありがとうございます。

4
Zach

昨日その日のために2つのプラグインを作成しました。

フィルター/コア

これが、検索クエリ部分がposts_searchフィルタ内でどのように見えるかです。

' AND (((wp_XX_posts.post_title LIKE '%test%') OR (wp_XX_posts.post_content LIKE '%test%'))) '

ここでwp_XX_は私のローカルMUインストール内の私のWPSEテストサイト用の$wpdb->prefixです。

プラグイン#1 - 私たちが必要としないドロップ検索投稿タイプ。

これは頻繁に必要とされるので、これは検索された投稿タイプを修正するプラグインです。

<?php
/** Plugin Name: (#66815) »kaiser« Limit search query post types */

/**
 * Alter the searched post types
 * 
 * @param  object $query
 * @return object $query
 */
add_action( 'pre_get_posts', 'wpse66815_pre_get_posts' );
function wpse66815_pre_get_posts( $query )
{
    if ( $query->is_main_query() )
    {
        $query->set( 'post_type', 'YOUR_POST_TYPE' );
    }

    return $query;
}

プラグイン#2 - 検索文字列を修正する

これで、投稿のタイトルとコンテンツのデフォルトの検索文字列がどのように見えるかがわかったので、必要な方法でそれを再構築する必要があります。

<?php 
/** Plugin Name: (#66815) »kaiser« Modify search query string */

function wpse66815_search_query_string( $search_string )
{
    global $wpdb;

    $searched_for = preg_match_all(
        // Match a prefix (%), but exclude it from the capture
        // Any character, any number of repetitions
        // Match a suffix (%), but exclude it from the capture
        "/(?<=\%)(.*)(?=\%)/",
        $search_string,
        $search_string_matches
    );

    // We only need one element
    $searched_for = array_shift( $search_string_matches );

    // Now we need to search for [LETTERS (min 1)][NUMBER (zero or more)][CHARACTER (zero or more)]
    preg_match_all(
        "/([^a-zA-Z][\d+]*[-_ ]*)/",
        $searched_for,
        $string_part_matches
    );

    // Here we now got matches - if not, we can simply abort and leave the default
    $searched_for = array_shift( $string_part_matches );
    if ( empty( $searched_for ) )
        return $search_string;

    // Finally we need to split the string by all parts that are allowed
    // YOU NEED TO EDIT MY ANSWER HERE AND FILL IN WHAT WORKS FOR YOU
    $keywords = preg_split(
         "/([\s]*[\d+]*[-_ ]*)/",
         $searched_for,
         -1, // 0 & -1 are NO limit
         PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
    );

    // Now loop and build an array for further processing
    // Our first search string is - of course - the default string
    $string_parts = array( $search_string );
    foreach ( $keywords as $keyword )
    {
        $new_string_parts[] = $GLOBALS['wpdb']->prepare(
            " AND ((%s.post_title LIKE '%s') OR (%s.post_content LIKE '%s')) ",
            $wpdb->posts,
            $wpdb->esc_like( $keyword ),
            $wpdb->posts,
            $wpdb->esc_like( $keyword )
        );
    }

    // Now lets glue them together, return and see what we get...
    return implode( " ", $new_string_parts );
}

この2nd プラグインはテストされていません、私のニーズは異なっていて、正規表現のいくつかは完全にあなたが必要としているものではないと思いますA)私が見つけた正規表現を構築するための素晴らしいツールは Expresso です。それは(ウェブの存在として)醜いものですが、非常に役に立ちます。

3
kaiser