relevanssi_hits_filter

apply_filters( 'relevanssi_hits_filter', array $data )

Filters all the result post objects found.

Parameters

$data
(array) An array of post objects in the index 0 and the search query in the index 1 (yes, the query should be a separate parameter, but legacy code is legacy code). Note that in some cases the array may contain just post IDs, ID=>parent pairs or ID=>type pairs. Make sure your code works with all, and returns the correct type!

More information

This is one of the key filter hooks in Relevanssi and one of the sources of the strong customization power in Relevanssi. It can be used for sorting, filtering and modifying post objects. This filter runs in the relevanssi_do_query() function after the searching is done. After this filter hook, Relevanssi takes out a slice of the post object array this filter hook filters according to the paging and posts_per_page parameters, and then adds excerpts and highlighting to those posts.

Basic use

Here’s the basic template of using this filter hook:

add_filter( 'relevanssi_hits_filter', 'rlv_hits_filter' );
function rlv_hits_filter( array $hits ) : array {
  $kept_posts = array();
  foreach ( $hits[0] as $_post ) {
    if ( $_post matches criteria ) {
      $kept_posts[] = $_post;
    }
  }
  $hits[0] = $kept_posts;
  return $hits;
}

You get an array of post objects, manipulate it somehow, and then return it. The post objects are in the order of relevance, so if you don’t want to resort them in some way, make sure you keep the relative order of the posts.

Return the full parameter array. Relevanssi doesn’t need the search query in the index 1, but other filter functions on this hook may require it, so keep it in place.

Note that if you have searches that use the fields parameter to have the search return just post IDs or ID=>parent pairs, you need to consider that in your relevanssi_hits_filter function: make sure you don’t assume the array to contain post objects, and make sure you return the same format you get. Relevanssi Related posts does this, so if you use related posts, you need to take note of this when doing your relevanssi_hits_filter functions.

Relevanssi has a useful utility function for this: relevanssi_get_an_object() takes as a parameter a post object, ID number or a **type**id string, and returns a post object and the format of the original parameter:

add_filter( 'relevanssi_hits_filter', 'rlv_nice_hits_filter' );
function rlv_nice_hits_filter( $hits ) {
  $by_author_23 = array();
  $by_others    = array();
  foreach ( $hits[0] as $_post ) {
    $object_array = relevanssi_get_an_object( $_post );
    $post_object  = $object_array['object'];
    $format       = $object_array['format']; // Often not required.
    
    if ( 23 === $post_object->post_author ) {
      $by_author_23[] = $_post; // Use $_post, because it's always in the right format.
    } else {
      $by_others[] = $_post;
    }
  }
  $hits[0] = array_merge( $by_author_23, $by_others );
  return $hits;
}

Filtering

In general, it’s best to filter things earlier than this. The earlier you can filter, the better it is for performance. Here are places where you can filter things before relevanssi_hits_filter:

  1. relevanssi_do_not_index. If there are posts that will always be filtered out, filter them out in the indexing.
  2. Query restrictions. If you have a restriction that can be expressed as a taxonomy filter (tax_query), a custom field filter (meta_query) or with other WP_Query parameters, set them there.
  3. relevanssi_where and relevanssi_join can be used to add other MySQL-based restrictions, like you would with posts_where and posts_join.
  4. relevanssi_match can filter out posts, especially based on post ID.
  5. relevanssi_post_ok also gets the post ID, and is a simple filter for filtering out posts. This is used by Relevanssi to provide post access compatibility for membership plugins.
  6. relevanssi_results is mostly a sorting hook, but can also be used for filtering out posts by post ID (but if you’re doing that, you should do it in relevanssi_post_ok anyway).

All of these are better places for filtering posts than using relevanssi_hits_filter. However, sometimes you want more complicated filtering procedures, in which case using relevanssi_hits_filter may be the best spot.

Filtering with relevanssi_hits_filter is straightforward. You get an array of posts, you return an array of posts, with the posts you don’t want filtered out, while keeping the relative order of the posts you keep intact.

Sorting

While this filter hook can be used for filtering posts, it’s often more useful for sorting posts. There are other ways to sort the posts:

  1. orderby and order parameters. If you have something simple where you can just specify an orderby parameter and have Relevanssi sort the posts in the correct order, use that.
  2. relevanssi_match and relevanssi_results can be used to affect the order by changing the post weights. relevanssi_match is based on individual search terms, relevanssi_results is based on post ID. These aren’t very good for setting a specific order, but work well for boosting some aspects and weighing some aspects less.

Fewer options here than in filtering, and in many cases relevanssi_hits_filter is the only way you can implement the sorting. You get full control here, and can move individual posts around freely. A common way to sort posts here is to group the results in different buckets, keeping the relative order of the posts in each bucket unchanged, then putting the buckets back together in the desired order. Here’s an example:

add_filter( 'relevanssi_hits_filter', 'rlv_bucket_sorting' );
function rlv_bucket_sorting( $hits ) {
  $posts_by_author_23   = array();
  $pages                = array();
  $posts_from_this_year = array();
  $rest_of_the_posts    = array();
  foreach ( $hits[0] as $_post ) {
    $post_object = relevanssi_get_an_object( $_post )['object'];
    
    if ( 23 === $post_object->post_author ) {
      $posts_by_author_23[] = $_post;
    } elseif ( 'page' === $post_object->post_type ) {
      $pages[] = $_post;
    } elseif ( date( 'Y' ) === date( 'Y', strtotime( $post_object->post_date_gmt ) ) ) {
      $posts_from_this_year[] = $_post;
    } else {
      $rest_of_the_posts[] = $_post;
    }
  }
  $hits[0] = array_merge( $posts_by_author_23, $pages, $posts_from_this_year, $rest_of_the_posts );
  return $hits;
}

This will sort the results by three completely separate aspects; this could not be done with other sorting methods, at least not this easy.

Here’s how you can lift one category of posts on top of the results:

add_filter( 'relevanssi_hits_filter', 'reviews_first' );
function reviews_first( $hits ) {
  $reviews         = array();
  $everything_else = array();
  foreach ( $hits[0] as $_post ) {
    $post_object = relevanssi_get_an_object( $_post )['object'];    
    $review = false;
    foreach ( get_the_category( $post_object->ID ) as $cat ) {
      if ( REVIEW_CATEGORY === $cat->cat_ID ) {
        $review = true;
        break;
      }
    }
    $review ? array_push( $reviews, $_post ) : array_push( $everything_else, $_post );
  }
  $hits[0] = array_merge( $reviews, $everything_else );
  return $hits;
}

Default use

Relevanssi has following filters on this filter hook:

  • relevanssi_polylang_term_filter – removes taxonomy terms that are in the wrong language from the Polylang results if all languages are not allowed.
  • relevanssi_wpml_filter – if the search is restricted to the current language, removes the wrong language results.
  • relevanssi_pinning – moves the pinned posts to the top of the results.

Examples of relevanssi_hits_filter use