Allow excluded keywords when searching posts.
Pass a keyword with a leading hyphen to exclude posts containing that keyword. For example, 'taco -onions' will return posts that contain the word 'taco' but do not contain the word 'onions'. Props akibjorklund. Fixes #33988. git-svn-id: https://develop.svn.wordpress.org/trunk@34934 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
ba7dc01639
commit
9c9cdf28ca
@ -1509,7 +1509,8 @@ class WP_Query {
|
||||
* @since 1.5.0
|
||||
* @since 4.2.0 Introduced the ability to order by specific clauses of a `$meta_query`, by passing the clause's
|
||||
* array key to `$orderby`.
|
||||
* @since 4.4.0 Introduced `$post_name__in` and `$title` parameters.
|
||||
* @since 4.4.0 Introduced `$post_name__in` and `$title` parameters. `$s` was updated to support excluded
|
||||
* search terms, by prepending a hyphen.
|
||||
* @access public
|
||||
*
|
||||
* @param string|array $query {
|
||||
@ -1587,7 +1588,9 @@ class WP_Query {
|
||||
* @type int $posts_per_archive_page The number of posts to query for by archive page. Overrides
|
||||
* 'posts_per_page' when is_archive(), or is_search() are true.
|
||||
* @type array $post_name__in An array of post slugs that results must match.
|
||||
* @type string $s Search keyword.
|
||||
* @type string $s Search keyword(s). Prepending a term with a hyphen will
|
||||
* exclude posts matching that term. Eg, 'pillow -sofa' will
|
||||
* return posts containing 'pillow' but not 'sofa'.
|
||||
* @type int $second Second of the minute. Default empty. Accepts numbers 0-60.
|
||||
* @type bool $sentence Whether to search by phrase. Default false.
|
||||
* @type bool $suppress_filters Whether to suppress filters. Default false.
|
||||
@ -2158,13 +2161,24 @@ class WP_Query {
|
||||
$searchand = '';
|
||||
$q['search_orderby_title'] = array();
|
||||
foreach ( $q['search_terms'] as $term ) {
|
||||
if ( $n ) {
|
||||
// Terms prefixed with '-' should be excluded.
|
||||
$include = '-' !== substr( $term, 0, 1 );
|
||||
if ( $include ) {
|
||||
$like_op = 'LIKE';
|
||||
$andor_op = 'OR';
|
||||
} else {
|
||||
$like_op = 'NOT LIKE';
|
||||
$andor_op = 'AND';
|
||||
$term = substr( $term, 1 );
|
||||
}
|
||||
|
||||
if ( $n && $include ) {
|
||||
$like = '%' . $wpdb->esc_like( $term ) . '%';
|
||||
$q['search_orderby_title'][] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $like );
|
||||
}
|
||||
|
||||
$like = $n . $wpdb->esc_like( $term ) . $n;
|
||||
$search .= $wpdb->prepare( "{$searchand}(($wpdb->posts.post_title LIKE %s) OR ($wpdb->posts.post_content LIKE %s))", $like, $like );
|
||||
$search .= $wpdb->prepare( "{$searchand}(($wpdb->posts.post_title $like_op %s) $andor_op ($wpdb->posts.post_content $like_op %s))", $like, $like );
|
||||
$searchand = ' AND ';
|
||||
}
|
||||
|
||||
@ -2264,11 +2278,19 @@ class WP_Query {
|
||||
|
||||
if ( $q['search_terms_count'] > 1 ) {
|
||||
$num_terms = count( $q['search_orderby_title'] );
|
||||
$like = '%' . $wpdb->esc_like( $q['s'] ) . '%';
|
||||
|
||||
// If the search terms contain negative queries, don't bother ordering by sentence matches.
|
||||
$like = '';
|
||||
if ( ! preg_match( '/(?:\s|^)\-/', $q['s'] ) ) {
|
||||
$like = '%' . $wpdb->esc_like( $q['s'] ) . '%';
|
||||
}
|
||||
|
||||
$search_orderby = '(CASE ';
|
||||
|
||||
// sentence match in 'post_title'
|
||||
$search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_title LIKE %s THEN 1 ", $like );
|
||||
if ( $like ) {
|
||||
$search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_title LIKE %s THEN 1 ", $like );
|
||||
}
|
||||
|
||||
// sanity limit, sort as sentence when more than 6 terms
|
||||
// (few searches are longer than 6 terms and most titles are not)
|
||||
@ -2281,7 +2303,9 @@ class WP_Query {
|
||||
}
|
||||
|
||||
// sentence match in 'post_content'
|
||||
$search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_content LIKE %s THEN 4 ", $like );
|
||||
if ( $like ) {
|
||||
$search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_content LIKE %s THEN 4 ", $like );
|
||||
}
|
||||
$search_orderby .= 'ELSE 5 END)';
|
||||
} else {
|
||||
// single word or sentence search
|
||||
|
@ -58,4 +58,71 @@ class Tests_Query_Search extends WP_UnitTestCase {
|
||||
function filter_wp_search_stopwords() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 33988
|
||||
*/
|
||||
public function test_s_should_exclude_term_prefixed_with_dash() {
|
||||
$p1 = $this->factory->post->create( array(
|
||||
'post_status' => 'publish',
|
||||
'post_content' => 'This post has foo but also bar',
|
||||
) );
|
||||
$p2 = $this->factory->post->create( array(
|
||||
'post_status' => 'publish',
|
||||
'post_content' => 'This post has only foo',
|
||||
) );
|
||||
|
||||
$q = new WP_Query( array(
|
||||
's' => 'foo -bar',
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$this->assertEqualSets( array( $p2 ), $q->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 33988
|
||||
*/
|
||||
public function test_s_should_exclude_first_term_if_prefixed_with_dash() {
|
||||
$p1 = $this->factory->post->create( array(
|
||||
'post_status' => 'publish',
|
||||
'post_content' => 'This post has foo but also bar',
|
||||
) );
|
||||
$p2 = $this->factory->post->create( array(
|
||||
'post_status' => 'publish',
|
||||
'post_content' => 'This post has only bar',
|
||||
) );
|
||||
|
||||
$q = new WP_Query( array(
|
||||
's' => '-foo bar',
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$this->assertEqualSets( array( $p2 ), $q->posts );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 33988
|
||||
*/
|
||||
public function test_s_should_not_exclude_for_dashes_in_the_middle_of_words() {
|
||||
$p1 = $this->factory->post->create( array(
|
||||
'post_status' => 'publish',
|
||||
'post_content' => 'This post has foo but also bar',
|
||||
) );
|
||||
$p2 = $this->factory->post->create( array(
|
||||
'post_status' => 'publish',
|
||||
'post_content' => 'This post has only bar',
|
||||
) );
|
||||
$p3 = $this->factory->post->create( array(
|
||||
'post_status' => 'publish',
|
||||
'post_content' => 'This post has only foo-bar',
|
||||
) );
|
||||
|
||||
$q = new WP_Query( array(
|
||||
's' => 'foo-bar',
|
||||
'fields' => 'ids',
|
||||
) );
|
||||
|
||||
$this->assertEqualSets( array( $p3 ), $q->posts );
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user