Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add highlighting to search results #31

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 121 additions & 3 deletions includes/class-algolia-search.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

class Algolia_Search {

/**
* @var array
*/
private $current_page_hits = [];

/**
* @var int
*/
private $nb_hits;
private $total_hits;

/**
* @var Algolia_Index
Expand All @@ -18,7 +23,9 @@ class Algolia_Search {
public function __construct( Algolia_Index $index ) {
$this->index = $index;

add_action( 'loop_start', [ $this, 'begin_highlighting' ] );
add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
add_action( 'wp_head', [ $this, 'output_highlighting_bundled_styles' ] );
}

/**
Expand Down Expand Up @@ -56,6 +63,8 @@ public function pre_get_posts( WP_Query $query ) {
'attributesToRetrieve' => 'post_id',
'hitsPerPage' => $posts_per_page,
'page' => $current_page - 1, // Algolia pages are zero indexed.
'highlightPreTag' => '<em class="algolia-search-highlight">',
'highlightPostTag' => '</em>',
)
);

Expand All @@ -73,9 +82,14 @@ public function pre_get_posts( WP_Query $query ) {
add_filter( 'found_posts', array( $this, 'found_posts' ), 10, 2 );
add_filter( 'posts_search', array( $this, 'posts_search' ), 10, 2 );

// Store the current page hits, so that we can use them for highlighting later on.
foreach ( $results['hits'] as $hit ) {
$this->current_page_hits[ $hit['post_id'] ] = $hit;
}

// Store the total number of its, so that we can hook into the `found_posts`.
// This is useful for pagination.
$this->nb_hits = $results['nbHits'];
$this->total_hits = $results['nbHits'];

$post_ids = array();
foreach ( $results['hits'] as $result ) {
Expand Down Expand Up @@ -117,7 +131,7 @@ public function pre_get_posts( WP_Query $query ) {
* @return int
*/
public function found_posts( $found_posts, WP_Query $query ) {
return $this->should_filter_query( $query ) ? $this->nb_hits : $found_posts;
return $this->should_filter_query( $query ) ? $this->total_hits : $found_posts;
}

/**
Expand All @@ -134,4 +148,108 @@ public function found_posts( $found_posts, WP_Query $query ) {
public function posts_search( $search, WP_Query $query ) {
return $this->should_filter_query( $query ) ? '' : $search;
}

/**
* Output the bundled styles for highlighting search result matches, if enabled.
*/
public function output_highlighting_bundled_styles() {
if ( ! $this->highlighting_enabled() ) {
return;
}

if ( ! apply_filters( 'algolia_search_highlighting_enable_bundled_styles', true ) ) {
return;
}

?>
<style>
.algolia-search-highlight {
background-color: #fffbcc;
border-radius: 2px;
font-style: normal;
}
</style>
<?php
}

/**
* Begin highlighting search result matches, if enabled.
*
* This method is called on the loop_start action, where we want to begin highlighting search result matches.
*
* @param WP_Query $query
*/
public function begin_highlighting( $query ) {
if ( ! $this->should_filter_query( $query ) ) {
return;
}

if ( ! $this->highlighting_enabled() ) {
return;
}

add_filter( 'the_title', [ $this, 'highlight_the_title' ], 10, 2 );
add_filter( 'get_the_excerpt', [ $this, 'highlight_get_the_excerpt' ], 10, 2 );

add_action( 'loop_end', [ $this, 'end_highlighting' ] );
}

/**
* Stop highlighting search result matches.
*
* This method is called on the loop_end action, where we want to stop highlighting search result matches.
*
* @param WP_Query $query
*/
public function end_highlighting( $query ) {
remove_filter( 'the_title', [ $this, 'highlight_the_title' ], 10 );
remove_filter( 'get_the_excerpt', [ $this, 'highlight_get_the_excerpt' ], 10 );

remove_action( 'loop_end', [ $this, 'end_highlighting' ] );
}

/**
* Filter the_title, replacing it with the highlighted title from the Algolia index.
*
* @param string $title
* @param int $post_id
*
* @return string
*/
public function highlight_the_title( $title, $post_id ) {
$highlighted_title = $this->current_page_hits[ $post_id ]['_highlightResult']['post_title']['value'] ?? null;

if ( ! empty( $highlighted_title ) ) {
$title = $highlighted_title;
}

return $title;
}

/**
* Filter get_the_excerpt, replacing it with the highlighted excerpt from the Algolia index.
*
* @param string $excerpt
* @param WP_Post $post
*
* @return string
*/
public function highlight_get_the_excerpt( $excerpt, $post ) {
$highlighted_excerpt = $this->current_page_hits[ $post->ID ]['_snippetResult']['content']['value'] ?? null;

if ( ! empty( $highlighted_excerpt ) ) {
$excerpt = $highlighted_excerpt;
}

return $excerpt;
}

/**
* Determine whether highlighting is enabled.
*
* @return bool
*/
private function highlighting_enabled() : bool {
return apply_filters( 'algolia_search_highlighting_enabled', true );
}
}
2 changes: 1 addition & 1 deletion includes/indices/class-algolia-searchable-posts-index.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ protected function get_settings() {
),
'attributesToSnippet' => array(
'post_title:30',
'content:30',
'content:' . intval( apply_filters( 'excerpt_length', 55 ) ),
),
'snippetEllipsisText' => '…',
);
Expand Down