Making WordPress.org


Ignore:
Timestamp:
04/12/2016 09:38:46 PM (9 years ago)
Author:
coffee2code
Message:

developer.wordpress.org: Improve ordering of search results by title relevance.

  • Show shortest full matches first.
  • Convert spaces to underscores when doing title searches for short phrases.

Props keesiemeijer.
Fixes #1516.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/themes/pub/wporg-developer/functions.php

    r2818 r2938  
    9898    add_filter( 'post_type_link', __NAMESPACE__ . '\\method_permalink', 10, 2 );
    9999    add_filter( 'term_link', __NAMESPACE__ . '\\taxonomy_permalink', 10, 3 );
     100    add_filter( 'posts_orderby', __NAMESPACE__ . '\\search_posts_orderby', 10, 2 );
    100101    add_filter( 'the_posts', __NAMESPACE__ . '\\rerun_empty_exact_search', 10, 2 );
    101102
     
    203204    if ( $query->is_search() ) {
    204205
    205         // Order searches by title first.
    206         $query->set( 'orderby', '' );
    207         $query->set( 'search_orderby_title', 1 );
    208 
    209206        // If user has '()' at end of a search string, assume they want a specific function/method.
    210207        $s = htmlentities( $query->get( 's' ) );
     
    221218
    222219/**
     220 * Filter the SQL for the ORDER BY clause for search queries.
     221 *
     222 * Adds ORDER BY condition with spaces replaced with underscores in 'post_title'.
     223 * Adds ORDER BY condition to order by title length.
     224 *
     225 * @param string   $orderby The ORDER BY clause of the query.
     226 * @param WP_Query $query   The WP_Query instance (passed by reference).
     227 * @return string  Filtered order by clause
     228 */
     229function search_posts_orderby( $orderby, $query ) {
     230    global $wpdb;
     231
     232    if ( $query->is_main_query() && is_search() && ! $query->get( 'exact' ) ) {
     233
     234        $search_order_by_title = $query->get( 'search_orderby_title' );
     235
     236        // Check if search_orderby_title is set by WP_Query::parse_search.
     237        if ( is_array( $search_order_by_title ) && $search_order_by_title ) {
     238
     239            // Get search orderby query.
     240            $orderby = parse_search_order( $query->query_vars );
     241
     242            // Add order by title length.
     243            $orderby .= " , CHAR_LENGTH( $wpdb->posts.post_title ) ASC, $wpdb->posts.post_title ASC";
     244        }
     245    }
     246
     247    return $orderby;
     248}
     249
     250/**
     251 * Generate SQL for the ORDER BY condition based on passed search terms.
     252 *
     253 * Similar to WP_Query::parse_search_order.
     254 * Adds ORDER BY condition with spaces replaced with underscores in 'post_title'.
     255 *
     256 * @global wpdb $wpdb WordPress database abstraction object.
     257 *
     258 * @param array   $q Query variables.
     259 * @return string ORDER BY clause.
     260 */
     261function parse_search_order( $q ) {
     262    global $wpdb;
     263
     264    if ( $q['search_terms_count'] > 1 ) {
     265        $num_terms = count( $q['search_orderby_title'] );
     266
     267        // If the search terms contain negative queries, don't bother ordering by sentence matches.
     268        $like = $_like = '';
     269        if ( ! preg_match( '/(?:\s|^)\-/', $q['s'] ) ) {
     270            $like = '%' . $wpdb->esc_like( $q['s'] ) . '%';
     271        }
     272
     273        $search_orderby = '';
     274
     275        // Sentence match in 'post_title'.
     276        if ( $like ) {
     277            $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_title LIKE %s THEN 1 ", $like );
     278            $_like =  str_replace( '-', '_', sanitize_title_with_dashes( $q['s'] ) );
     279            $_like = '%' . $wpdb->esc_like( $_like ) . '%';
     280            if ( $_like !== $like ) {
     281                // Sentence match in 'post_title' with spaces replaced with underscores.
     282                $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_title LIKE %s THEN 2 ", $_like );
     283            }
     284        }
     285
     286        // Sanity limit, sort as sentence when more than 6 terms.
     287        // (few searches are longer than 6 terms and most titles are not)
     288        if ( $num_terms < 7 ) {
     289            // all words in title
     290            $search_orderby .= 'WHEN ' . implode( ' AND ', $q['search_orderby_title'] ) . ' THEN 3 ';
     291            // any word in title, not needed when $num_terms == 1
     292            if ( $num_terms > 1 )
     293                $search_orderby .= 'WHEN ' . implode( ' OR ', $q['search_orderby_title'] ) . ' THEN 4 ';
     294        }
     295
     296        // Sentence match in 'post_content'.
     297        if ( $like ) {
     298            $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_content LIKE %s THEN 5 ", $like );
     299        }
     300
     301        if ( $search_orderby ) {
     302            $search_orderby = '(CASE ' . $search_orderby . 'ELSE 6 END)';
     303        }
     304    } else {
     305        // Single word or sentence search.
     306        $search_orderby = reset( $q['search_orderby_title'] ) . ' DESC';
     307    }
     308
     309    return $search_orderby;
     310}
     311
     312/**
    223313 * Rerun an exact search with the same criteria except exactness if no posts
    224314 * were found.
Note: See TracChangeset for help on using the changeset viewer.