0

Somewhen in recent history WordPress altered the default search query from titles-only to titles, excerpts, and post content as well. This works great on small sites but on sites with many posts or very long post contents the search time can be unacceptably long.

So how do we modify the default WordPress search to limit it to searching post titles alone and not the post excerpt or content?

On my current project with 100,000 articles, for instance, a search takes 32 seconds to run. I'm also using an ACF relationship field and the typeahead search is painfully slow. The problem seems to be the default query for /?s which searches post_title, post_excerpt, and post_content.

I found many articles and SE posts regarding expand the query, for instance to include post_meta fields, but nothing conclusive about limiting it to a post_title search.

Solution could be a custom function or a plugin if the plugin alters the search function for all queries including ACF relationship queries.

On a stock WordPress 4.9 search, Query Monitor reports the query as follows:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts 
WHERE 1=1 
AND (((wp_posts.post_title LIKE '%foo%')
OR (wp_posts.post_excerpt LIKE '%foo%')
OR (wp_posts.post_content LIKE '%foo%'))
AND ((wp_posts.post_title LIKE '%bar%')
OR (wp_posts.post_excerpt LIKE '%bar%')
OR (wp_posts.post_content LIKE '%bar%'))) 
AND wp_posts.post_type IN ('post', 'page', 'attachment')
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_author = 1
AND wp_posts.post_status = 'private') 
ORDER BY (CASE
WHEN wp_posts.post_title LIKE '%foo bar%'
THEN 1
WHEN wp_posts.post_title LIKE '%foo%'
AND wp_posts.post_title LIKE '%bar%'
THEN 2
WHEN wp_posts.post_title LIKE '%foo%'
OR wp_posts.post_title LIKE '%bar%'
THEN 3
WHEN wp_posts.post_excerpt LIKE '%foo bar%'
THEN 4
WHEN wp_posts.post_content LIKE '%foo bar%'
THEN 5
ELSE 6 END), wp_posts.post_date DESC
LIMIT 0, 10

The closest I've come to an answer is to modify pre_get_post, but the Codex doesn't explain how to alter the fields that are searched. I've also found this solution but it doesn't work at all.

EDIT: Getting closer. Here's the line I need to modify in wp-includes/class-wp-query.php:

$search .= $wpdb->prepare( "{$searchand}(({$wpdb->posts}.post_title $like_op %s) $andor_op ({$wpdb->posts}.post_excerpt $like_op %s) $andor_op ({$wpdb->posts}.post_content $like_op %s))", $like, $like, $like );

0

After searching through two dozen blog posts I was able to combined several approaches and make this work.

function my_search_by_title_only( $search, $wp_query ) {
  if ( ! empty( $search ) && ! empty( $wp_query->query_vars['search_terms'] ) ) {
    global $wpdb;
    $q = $wp_query->query_vars;
    $n = ! empty( $q['exact'] ) ? '' : '%';
    $search = array();
    foreach ( ( array ) $q['search_terms'] as $term )
    $search[] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $n . $wpdb->esc_like( $term ) . $n );
    if ( ! is_user_logged_in() )
    $search[] = "$wpdb->posts.post_password = ''";
    $search = ' AND ' . implode( ' AND ', $search );
  }
  return $search;
}
add_filter( 'posts_search', 'my_search_by_title_only', 10, 2 );

Unlike other, older solutions, this will not adversely affect things like menus or the latest blog posts page.

This will match the search term in post titles only, massively speeding searches on sites with large numbers of posts. This works on both the front end and admin so it's also useful when ACF Relationship Field lookups are really slow.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.