Make WordPress Core

Changeset 34934


Ignore:
Timestamp:
10/08/2015 03:17:22 AM (9 years ago)
Author:
boonebgorges
Message:

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.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/query.php

    r34903 r34934  
    15101510     * @since 4.2.0 Introduced the ability to order by specific clauses of a `$meta_query`, by passing the clause's
    15111511     *              array key to `$orderby`.
    1512      * @since 4.4.0 Introduced `$post_name__in` and `$title` parameters.
     1512     * @since 4.4.0 Introduced `$post_name__in` and `$title` parameters. `$s` was updated to support excluded
     1513     *              search terms, by prepending a hyphen.
    15131514     * @access public
    15141515     *
     
    15881589     *                                                 'posts_per_page' when is_archive(), or is_search() are true.
    15891590     *     @type array        $post_name__in           An array of post slugs that results must match.
    1590      *     @type string       $s                       Search keyword.
     1591     *     @type string       $s                       Search keyword(s). Prepending a term with a hyphen will
     1592     *                                                 exclude posts matching that term. Eg, 'pillow -sofa' will
     1593     *                                                 return posts containing 'pillow' but not 'sofa'.
    15911594     *     @type int          $second                  Second of the minute. Default empty. Accepts numbers 0-60.
    15921595     *     @type bool         $sentence                Whether to search by phrase. Default false.
     
    21592162        $q['search_orderby_title'] = array();
    21602163        foreach ( $q['search_terms'] as $term ) {
    2161             if ( $n ) {
     2164            // Terms prefixed with '-' should be excluded.
     2165            $include = '-' !== substr( $term, 0, 1 );
     2166            if ( $include ) {
     2167                $like_op  = 'LIKE';
     2168                $andor_op = 'OR';
     2169            } else {
     2170                $like_op  = 'NOT LIKE';
     2171                $andor_op = 'AND';
     2172                $term     = substr( $term, 1 );
     2173            }
     2174
     2175            if ( $n && $include ) {
    21622176                $like = '%' . $wpdb->esc_like( $term ) . '%';
    21632177                $q['search_orderby_title'][] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $like );
     
    21652179
    21662180            $like = $n . $wpdb->esc_like( $term ) . $n;
    2167             $search .= $wpdb->prepare( "{$searchand}(($wpdb->posts.post_title LIKE %s) OR ($wpdb->posts.post_content LIKE %s))", $like, $like );
     2181            $search .= $wpdb->prepare( "{$searchand}(($wpdb->posts.post_title $like_op %s) $andor_op ($wpdb->posts.post_content $like_op %s))", $like, $like );
    21682182            $searchand = ' AND ';
    21692183        }
     
    22652279        if ( $q['search_terms_count'] > 1 ) {
    22662280            $num_terms = count( $q['search_orderby_title'] );
    2267             $like = '%' . $wpdb->esc_like( $q['s'] ) . '%';
     2281
     2282            // If the search terms contain negative queries, don't bother ordering by sentence matches.
     2283            $like = '';
     2284            if ( ! preg_match( '/(?:\s|^)\-/', $q['s'] ) ) {
     2285                $like = '%' . $wpdb->esc_like( $q['s'] ) . '%';
     2286            }
    22682287
    22692288            $search_orderby = '(CASE ';
     2289
    22702290            // sentence match in 'post_title'
    2271             $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_title LIKE %s THEN 1 ", $like );
     2291            if ( $like ) {
     2292                $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_title LIKE %s THEN 1 ", $like );
     2293            }
    22722294
    22732295            // sanity limit, sort as sentence when more than 6 terms
     
    22822304
    22832305            // sentence match in 'post_content'
    2284             $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_content LIKE %s THEN 4 ", $like );
     2306            if ( $like ) {
     2307                $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_content LIKE %s THEN 4 ", $like );
     2308            }
    22852309            $search_orderby .= 'ELSE 5 END)';
    22862310        } else {
  • trunk/tests/phpunit/tests/query/search.php

    r31622 r34934  
    5959        return array();
    6060    }
     61
     62    /**
     63     * @ticket 33988
     64     */
     65    public function test_s_should_exclude_term_prefixed_with_dash() {
     66        $p1 = $this->factory->post->create( array(
     67            'post_status' => 'publish',
     68            'post_content' => 'This post has foo but also bar',
     69        ) );
     70        $p2 = $this->factory->post->create( array(
     71            'post_status' => 'publish',
     72            'post_content' => 'This post has only foo',
     73        ) );
     74
     75        $q = new WP_Query( array(
     76            's' => 'foo -bar',
     77            'fields' => 'ids',
     78        ) );
     79
     80        $this->assertEqualSets( array( $p2 ), $q->posts );
     81    }
     82
     83    /**
     84     * @ticket 33988
     85     */
     86    public function test_s_should_exclude_first_term_if_prefixed_with_dash() {
     87        $p1 = $this->factory->post->create( array(
     88            'post_status' => 'publish',
     89            'post_content' => 'This post has foo but also bar',
     90        ) );
     91        $p2 = $this->factory->post->create( array(
     92            'post_status' => 'publish',
     93            'post_content' => 'This post has only bar',
     94        ) );
     95
     96        $q = new WP_Query( array(
     97            's' => '-foo bar',
     98            'fields' => 'ids',
     99        ) );
     100
     101        $this->assertEqualSets( array( $p2 ), $q->posts );
     102    }
     103
     104    /**
     105     * @ticket 33988
     106     */
     107    public function test_s_should_not_exclude_for_dashes_in_the_middle_of_words() {
     108        $p1 = $this->factory->post->create( array(
     109            'post_status' => 'publish',
     110            'post_content' => 'This post has foo but also bar',
     111        ) );
     112        $p2 = $this->factory->post->create( array(
     113            'post_status' => 'publish',
     114            'post_content' => 'This post has only bar',
     115        ) );
     116        $p3 = $this->factory->post->create( array(
     117            'post_status' => 'publish',
     118            'post_content' => 'This post has only foo-bar',
     119        ) );
     120
     121        $q = new WP_Query( array(
     122            's' => 'foo-bar',
     123            'fields' => 'ids',
     124        ) );
     125
     126        $this->assertEqualSets( array( $p3 ), $q->posts );
     127    }
    61128}
Note: See TracChangeset for help on using the changeset viewer.