Make WordPress Core

Changeset 34934


Ignore:
Timestamp:
10/08/2015 03:17:22 AM (11 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.