WordPress 查询搜索 's' 精确单词

5
我想通过关键词来限制搜索,只搜索确切的单词而不是短语或混合内容。所以,如果我搜索“cheese”,它应该只限定为单词“cheese”,而现在它还会返回包含单词“cheeseburger”的内容。
我的修改模板查询中的搜索查询部分就在这里:
if ( ! function_exists( 'get_job_listings' ) ) :
    if ( 'top_rating' === $args['orderby'] ) {
        $query_args['meta_query'] = [
            'relation' => 'OR',
            [
                'key'     => 'rating',
                'compare' => 'EXISTS',
            ],
            [
                'key'     => 'rating',
                'compare' => 'NOT EXISTS',
            ],
        ];
        $query_args['orderby'] = [
            'meta_value_num' => 'DESC',
            'comment_count' => 'DESC',
        ];
    }

    $job_manager_keyword = sanitize_text_field( $args['search_keywords'] );
    if ( ! empty( $job_manager_keyword ) && strlen( $job_manager_keyword ) >= apply_filters( 'job_manager_get_listings_keyword_length_threshold', 2 ) ) {
        $search_query = $job_manager_keyword;
    $query_args['s'] = $search_query;
        // $query_args['s'] = '"' . $search_query . '"';
        // $query_args['s'] = '/\b' . preg_quote($search_query, '/') . '\b/';
        $query_args['sentence'] = true;
        add_filter( 'posts_search', 'get_job_listings_keyword_search' );
    }

    $query_args = apply_filters( 'job_manager_get_listings', $query_args, $args );

    if ( empty( $query_args['meta_query'] ) ) {
        unset( $query_args['meta_query'] );
    }

    if ( empty( $query_args['tax_query'] ) ) {
        unset( $query_args['tax_query'] );
    }

    /** This filter is documented in wp-job-manager.php */
    $query_args['lang'] = apply_filters( 'wpjm_lang', null );

    // Filter args.
    $query_args = apply_filters( 'get_job_listings_query_args', $query_args, $args );
    do_action( 'before_get_job_listings', $query_args, $args );

    // Cache results.
    if ( apply_filters( 'get_job_listings_cache_results', true ) ) {
        $to_hash              = wp_json_encode( $query_args );
        $query_args_hash      = 'jm_' . md5( $to_hash . JOB_MANAGER_VERSION ) . WP_Job_Manager_Cache_Helper::get_transient_version( 'get_job_listings' );
        $result               = false;
        $cached_query_results = true;
        $cached_query_posts   = get_transient( $query_args_hash );

        if ( is_string( $cached_query_posts ) ) {
            $cached_query_posts = json_decode( $cached_query_posts, false );

            if (
                $cached_query_posts
                && is_object( $cached_query_posts )
                && isset( $cached_query_posts->max_num_pages )
                && isset( $cached_query_posts->found_posts )
                && isset( $cached_query_posts->posts )
                && is_array( $cached_query_posts->posts )
            ) {
                if ( in_array( $query_args['fields'], [ 'ids', 'id=>parent' ], true ) ) {
                    // For these special requests, just return the array of results as set.
                    $posts = $cached_query_posts->posts;
                } else {
                    $posts = array_map( 'get_post', $cached_query_posts->posts );
                }

                $result = new WP_Query();
                $result->parse_query( $query_args );
                $result->posts         = $posts;
                $result->found_posts   = intval( $cached_query_posts->found_posts );
                $result->max_num_pages = intval( $cached_query_posts->max_num_pages );
                $result->post_count    = count( $posts );
            }
        }

        if ( false === $result ) {
            $result               = new WP_Query( $query_args );
            $cached_query_results = false;
            $cacheable_result                  = [];
            $cacheable_result['posts']         = array_values( $result->posts );
            $cacheable_result['found_posts']   = $result->found_posts;
            $cacheable_result['max_num_pages'] = $result->max_num_pages;
            set_transient( $query_args_hash, wp_json_encode( $cacheable_result ), DAY_IN_SECONDS );
        }

        if ( $cached_query_results ) {
            // random order is cached so shuffle them.
            if ( 'rand_featured' === $args['orderby'] ) {
                usort( $result->posts, '_wpjm_shuffle_featured_post_results_helper' );
            } elseif ( 'rand' === $args['orderby'] ) {
                shuffle( $result->posts );
            }
        }
    } else {
        $result = new WP_Query( $query_args );
    }
    do_action( 'after_get_job_listings', $query_args, $args );

    //remove_filter( 'posts_search', 'get_job_listings_keyword_search' );
    return $result;
}

endif;

我可以看到$query_args['s']是一个用于搜索关键词的标准查询,但是我尝试过一些标准查询修改,像这些例子: 还有其他的例子,但是它们都对我不起作用。
我应该如何修改它,以便只搜索内容中的确切单词? 我看到这个问题已经被多次发布,但是我尝试过的所有例子都没有起作用。

请问您能告诉我您在搜索中使用的主题和插件吗? - Behrooz shy
插件是"wp job manager",主题是listingeasy。搜索功能位于插件内部,代码来自插件。 - lonerunner
1
应该有一个地方可以指定一个正则表达式,就像你在那里尝试的'/\b'.preg_quote($search_query, '/').'\b/'。我的意思是,首先验证您需要传递一个解析为/\bterm\b/的分隔字符串,并且使该语法被解释为正在进行搜索的内容。如果这是阻碍您的原因,请创建一个简单的测试用例来使正则表达式起作用。我的意思是,为什么要这么折磨自己呢? - sln
3个回答

1
熟悉SQL语法后,定制WordPress执行的任何查询都会变得更容易。
您可以直接使用posts_clauses过滤器修改任何查询,包括搜索查询。
默认情况下,WordPress使用LIKE%将搜索词包裹在文章标题、内容和摘要中进行搜索,类似以下语法。
WHERE 1=1 
AND (
    post_table.post_title LIKE '%hello%' OR 
    post_table.post_excerpt LIKE '%hello%' OR
    post_table.content LIKE '%hello%'
)

它甚至将每个单词分成一个独立的LIKE语句,
但是由于您想要进行精确匹配,您可以在搜索项之间添加一个空格,然后再进行首尾匹配,类似下面的方式在post_content列中搜索字符串。
WHERE 1=1 
AND (
    post_table.post_content LIKE 'hello %' OR  //hello is first word, match on -> hello world
    post_table.post_content LIKE '% hello %' OR //hello is in between words, match on -> world hello world
    post_table.post_content LIKE '% hello' //hello is the last word, match on -> world hello
)

如果您使用的是支持正则表达式的MYSQL 8,那么您可以通过边界进行字符串搜索,类似于;
WHERE 1=1 
AND (
    post_table.post_content RLIKE "[[:<:]]hello[[:>:]]"
) 

现在要修改搜索查询非常简单,可以参考下面的代码。

add_filter( 'posts_clauses', function ($clauses, $query) {
    
    // prevent query modification if its not search page or an admin page or not main query
    if ( !$query->is_search || is_admin() || !$query->is_main_query()) 
            return $clauses;
    
    // get the search term
    $searchTerm = sanitize_text_field( $query->query['s'] );
    
    //build the search query, and perfom the search on post_title and post_content column
    //refer to two functions below
    $searchQuery = _custom_search_query($searchTerm, ['post_title', 'post_content']);
    
    //override wordpress default search query by defining the where key on its clauses
    $clauses['where'] = "AND ( $searchQuery )";
    
    // additional filter on what you want to search
    $clauses['where'] .= " AND wp_posts.post_type = 'post' AND wp_posts.post_status = 'publish'";
    
    //add the default order
    $clauses['orderby'] = "wp_posts.post_title DESC, wp_posts.post_date DESC";
    
    return $clauses;
}, 10, 2 );

为了避免手动输入所有的“LIKES”和“OR's”,我们可以根据您想要的查询方式使用下面这两个函数。
用于有条件地构建在指定字段上搜索的“LIKE”和“OR”语句。
function _custom_search_query( $term, $fields = [] ) {
    
    $query = '';
    $compares = ["$term %", "% $term", "% $term %"];
    foreach ($fields as $key => $field) {
        foreach ($compares as $k => $comp) {
            $query .= ($key > 0 || $k > 0 ? ' OR' : '' ). " wp_posts.$field LIKE '$comp'";
        }
    }
    return $query;
}

用于根据指定的不同字段进行条件构建RLIKEOR语句以进行搜索。
function _regex_custom_search_query(  $term, $fields = [] ) {
    $query = '';
    foreach ($fields as $key => $field) {
        $query .= ($key > 0 ? ' OR' : '' ). ' wp_posts.'.$field.' RLIKE "[[:<:]]'.$term.'[[:>:]]"' ;
    }
    return $query;
}

此外,在前端搜索页面上打印$wp_query->request,您将看到当前页面上执行的SQL语句,这将帮助您调试任何问题。
add_action('wp_head', function() {
    global $wp_query;

    if ( !$wp_query->is_search )
        return;
    
    echo '<pre>', print_r($wp_query->request, 1), '</pre>';
    
});

如果你也想创建自己的搜索页面,你可以简单地使用$wpdb应用相同的查询。

我尝试使用pre_get_posts来修改搜索功能,并且在其他网站上运行良好,但在这个网站上却无效。上述代码覆盖了其他代码并且不起作用,可能是因为这是自定义的文章类型。 - lonerunner

-1
为了在WordPress中限制搜索只匹配确切的单词而不是短语或混合内容,您可以在主题的functions.php文件或自定义插件中使用以下代码片段:
function exact_word_search_filter($query) {
    if ($query->is_search) {
        $search_terms = explode(' ', $query->query_vars['s']);
        $search_terms = array_map('esc_sql', $search_terms);
        $exact_search = implode(' ', $search_terms);
        $query->set('exact', true);
        $query->set('search_terms', "{$exact_search}");
        $query->set('search_terms_count', 1);
    }
    return $query;
}
add_filter('pre_get_posts', 'exact_word_search_filter');

此代码修改搜索查询,将exact参数设置为true,确保只有精确的单词匹配会包含在搜索结果中。

-1
要实现这个,你需要将posts_search钩子修改为正则表达式。这样才能匹配整个单词。而且你还需要进行自定义的SQL查询。
function keyword_search( $search, $wp_query ) {
    global $wpdb;

    if ( empty( $search ) )
        return $search;

    $q = $wp_query->query_vars;
    $n = ! empty( $q['exact'] ) ? '' : '%';
    $search = $searchand = '';

    foreach ( (array) $q['search_terms'] as $term ) {
        $term = $n . $wpdb->esc_like( $term ) . $n;
        $search .= $wpdb->prepare( "{$searchand}($wpdb->posts.post_title REGEXP '[[:<:]]{$term}[[:>:]]'", $term );
        $searchand = ' AND ';
    }
    return $search;
}

然后替换您的get_job_listing函数条件。
if ( ! empty( $job_manager_keyword ) && strlen( $job_manager_keyword ) >= apply_filters( 'job_manager_get_listings_keyword_length_threshold', 2 ) ) {
    $search_query = $job_manager_keyword;
    $query_args['s'] = $search_query;
    add_filter( 'posts_search', 'keyword_search', 500, 2 );
}

在搜索之后,请移除您的搜索过滤器。使用这个方法。
do_action( 'after_get_job_listings', $query_args, $args );
remove_filter( 'posts_search', 'keyword_search', 500 );

经过测试,对我有效。希望这也对你有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接