Custom excerpts on the WordPress search page highlighting query keywords

In this post I’ll share a custom function for WordPress, which will tweak the text excerpts in the search result page to always show the part of the text which includes the search query.

Usually Wordpess will only display the first words of the site or post in the search results page. This short excerpt might not include the search query.

Search results doesn’t include keyword “tux”

In the screenshot above, I searched for the keyword “tux” on this site and you’ll find posts containing this keyword. But the previews wouldn’t tell you in which context the keyword is present in the text.

I wrote a custom function which will show a part of the text which includes the search keyword and highlights it.

Search query “Egon Eiermann” highlighted on the search page

Copy following snippet into the functions.php of your WordPress theme:

# Highlight query in search results

function generate_excerpt($text, $query, $length) {

	$words = explode(' ', $text);
	$total_words = count($words);

	if ($total_words > $length) {

		$queryLow = array_map('strtolower', $query);
		$wordsLow = array_map('strtolower', $words);

		for ($i=0; $i <= $total_words; $i++) {

			foreach ($queryLow as $queryItem) {

				if (preg_match("/\b$queryItem\b/", $wordsLow[$i])) {
					$posFound = $i;
					break;
				}
			}

			if ($posFound) {
				break;
			}
		}

		if ($i > ($length+($length/2))) {
			$i = $i - ($length/2);
		} else {
      $i = 0;
    }

	}

	$cutword = array_splice($words,$i,$length);
	$excerpt = implode(' ', $cutword);

	$keys = implode('|', $query);
	$excerpt = preg_replace('/(' . $keys .')/iu', '<strong class="tx-indexedsearch-redMarkup">\0</strong>', $excerpt);
	$excerptRet = '<p>';
  if ($i !== 0) {
    $excerptRet .= '... ';
  }
  $excerptRet .= $excerpt . ' ...</p>';

	return $excerptRet;
  
}

function search_excerpt_highlight() {

    # Length in word count
    $excerptLength = 32;

    $text = wp_strip_all_tags( get_the_content() );

    # Filter double quotes from query. They will
    # work on the results side but won't help with
    # text highlighting and displaying.
    $query=get_search_query(false);
    $query=str_replace('"','',$query);
    $query=esc_html($query);

    $query = explode(' ', $query);

    echo generate_excerpt($text, $query, $excerptLength);

}

The variable $excerptLength will define the number of words shown in the excerpt.

If you don’t already have a custom search.php page, create one and replace the WordPress function the_excerpt() with our custom excerpt-function search_excerpt_highlight():

      <?php while ( have_posts() ) : the_post(); ?>
      <div class="tx-indexedsearch-res">
      	<h3>
      		<span class="tx-indexedsearch-title">
            <a href="<?php the_permalink(); ?>" ><?php the_title(); ?></a>
          </span>
      	</h3>
      	<p class="tx-indexedsearch-description">
          <!-- <?php the_excerpt(); ?> -->
          <?php search_excerpt_highlight(); ?>
        </p>
      	<a href="#" class="link-more"><span>Weiterlesen</span></a>
      </div>
      <?php endwhile; ?>

Until know I haven’t found a similar or better solution to solve this issue without any need for extra plugins.

💬 Are you interested in our work or have some questions? Join us in our public Signal chat pi crew 👋
🪙 If you like our work or want to supprot us, you can donate MobileCoins to our address.

Comments

  1. I have implement this code but getting error Undefined variable: posFound on following line.

    if ($posFound) {
    break;
    }

  2. Hii need help in above code.

    I paste above code in my website functions.php file but i am getting 3 error in this code. please help me. see the below 3 error
    —————————————————–
    Notice: Undefined variable: posFound

    if ($posFound) {
    break;
    }
    —————————————————–
    Notice: Undefined variable: i

    $cutword = array_splice($words,$i,$length);
    —————————————————–
    Notice: Undefined variable: i

    if ($i !== 0) {

    —————————————————–

    Waiting for your reply :)

  3. Oh seems to be an bug in the example code. Try adding following line

    $posFound = False;

    before:

    for ($i=0; $i <= $total_words; $i++) {

    So it looks like:

    $posFound = False;
    for ($i=0; $i <= $total_words; $i++) {

    Tell me if this works :)

  4. Hey onny,

    well done! It works superb on single words and longer words. So “train” works well. “At” or “train station” does not work so well.

    Example text: We were hiking in the woods. To get there we would take the train. Once we entered the conductor screamed attention and everybody listened. A little later the train left the station.

    I am not a coder but would love to make the code work in a way that it would prioritize exact matches and if it cant find any it would then try to match 2+ words strings within the same sentence.

    Any help with this would be much appreciated. If I find a solution in the meantime, I ll let you know as well.

    Thanks

    Hendrik

  5. @Hendrik: Good point, I’m sure there is room for improvement. I also noticed some minor drawbacks back then. If you could find any solutions to this I’m happy to update the post :)

Leave a Reply

Your email address will not be published. Required fields are marked *