Posted on

Search results breakdown by type

Do you want to have a breakdown of search results by post type? This could be used for example to show a list of post types like books, movies, albums on a review site, and make them links to limit the search results to that post type.

This is fairly easy to do with the relevanssi_hits_filter hook. Here’s a function that can be attached to the hook. It will fill a global variable with the post type breakdown:

add_filter( 'relevanssi_hits_filter', 'search_result_types' );
function search_result_types( $hits ) {
    global $hns_search_result_type_counts;
    $types = array();
    if ( ! empty( $hits ) ) {
        foreach ( $hits[0] as $hit ) {
            $types[ $hit->post_type ]++;
    $hns_search_result_type_counts = $types;
    return $hits;

Add the function to your theme functions.php. This function was written by Simon from Lumpy Lemon. Thanks to Simon for sharing the code!

An extended version

The code above has one problem. If the search is restricted to just one post type, the code won’t count the other post types: only those that are present in the search. If you want all post types counted, you can use this version of the function:

add_filter( 'relevanssi_hits_filter', 'search_result_types' );
function search_result_types( $hits ) {
    global $hns_search_result_type_counts, $wp_query, $rlv_doing_this_already;
    if ( ! $rlv_doing_this_already && 'any' !== $wp_query->query_vars['post_type'] ) {
    	$copy_query                          = $wp_query;
    	$copy_query->query_vars['post_type'] = 'any';
    	$rlv_doing_this_already              = true;
    	relevanssi_do_query( $copy_query );
    	return $hits;
    $types = array();
    if ( ! empty( $hits ) ) {
        foreach ( $hits[0] as $hit ) {
            $types[ $hit->post_type ]++;
    $hns_search_result_type_counts = $types;
    return $hits;

This version counts all post types.

31 comments Search results breakdown by type

  1. Great plugin!
    A question: How do i use this function with the relevanssi_hits_filter hook in order to separate results by type? I’m at a loss.

    Is there also an easy way to style the post types?

  2. The code was missing an add_filter() call. I’ve added that.

    Styling post types… Well, the post type is stored in $post->post_type, so you can find it there and use that to style it, for example using it to add a post type dependent class to a div that contains the post.

  3. Thank you for your prompt reply!

    I’ve pasted the code in functions.php, but I’m maybe supposed to do more steps – cause nothing changes?

    My usual approach would be to add multiple loops and style each loop separatly, but iguess that will not work. I’d like some post-types only show as links.

  4. Ah, sorry, misread your questions. This does not separate posts by post type, just creates an array that has the counts of posts per type.

    If you want to separate by type, you need to go through the $hits array, check each post and file it in the correct array by post type, then reconstruct the $hits array from the post type arrays in the order you desire.

    In your loop, check $post->post_type and style the post according to the type. So instead of multiple loops, you’ll have a single loop and a switch clause or something like that.

    1. Hi Mikko,

      can you please go in more detail here? I would like to show my custom post type “products” first, then pages and last posts.

        1. This function doesn’t do anything visible, that’s why.

          It just puts the data in the global variable $hns_search_result_type_counts. You need to write some code to do something with that data in order to see visible results.

          1. Hi Mikko. Can you guide me on how to access the data from that variable? I tried but failed.

          2. Add this to your search results template:

            global $hns_search_result_type_counts;

            It’ll show you what’s inside the variable.

          3. Thank you so much Mikko. My bad because I tried to access it without calling “global”.

  5. Thank you so much for this information! I was able to get it to work. However, I do have a question. Let’s say I have results from 3 post types: posts, media, and products. On the search landing page, it shows “# posts, # media, and # products”. However, when you click on products, let’s say, the hits in the function change, and instead of showing “# products” only. Any way of keeping the original counts for the entirety of the search on the filtered results pages?

    ETA: I’m using Emily Johnson’s pastebin code below, along with the function above.

    Thank you!

  6. Hey Mikko, thanks for the code. I’m on the latest version of WordPress and I get the error “Undefined index” from the line where it adds to the count. Any ideas why?

    1. Of course I figure it out right after I post this. I had to add the following to get the code above to work:

      $types[‘product’] = ”;

      $types[‘lookbook’] = ”;

      $types[‘post’] = ”;

  7. It looks like the extended version needs to be changed from query_vars[‘post_type’] to query_vars[‘post_types’] in order to work.

  8. hi Mikko,
    i am developing a products per page drop-down in woocommerce and am passing a variable with value ?my_select_ppp=60 at the url and it does set the posts_per_page as per the value when it’s not search but if the url has the search set like ?s=’stickers’&post_type=’product’ this variable my_select_ppp doesn’t get added to the given URL and doesn’t do the expected functionality.
    Can you help me in what needs to be done
    i hereby attach the code
    // my select option for no of products to be shown at the shop page


    function my_select(){
    global $wp_query;
    $per_page = filter_input(INPUT_GET,’perpage’,FILTER_SANITIZE_NUMBER_INT);
    echo “Products per Page:”;
    echo “”;
    $orderby_options = array(”=>”,’40’=>’40’,’60’=>’60’);
    foreach($orderby_options as $key=>$value){
    echo “$value”;
    echo “”;

    function my_pre_get_products_query($query){
    $per_page = filter_input(INPUT_GET,’ga_select_ppp’,FILTER_SANITIZE_NUMBER_INT);
    if($query->is_main_query() && !is_admin() ){



  9. You could also just use the following above your loop:

    if ( have_posts() ) {
    $post_types = wp_list_pluck( $posts, ‘post_type’ );
    $post_type_counts = array_count_values( $post_types );

  10. Hello! I tried using the extended version but I get different counts than the actual results. For example, I get a count of `1` for a `podcast` type, but when I go to the results I actually see two different posts.

    Any suggestions on how I can debug further?

    1. Arturo, I would check the parameters to the $copy_query, is there something that is restricting the search results to a smaller set of results? That would be a good place to start.

      1. Thank you for the quick reply Mikko!

        Coincidentally that was the first place I looked into. I used `var_dump` on the original $wp_query and $copy_query and then used a text diff tool to find the differences. The only difference is `post_type = any`, vs. `post_type = podcast`, as expected.

        Another approach I took was to count the results that were returned directly from the call to `relevanssi_do_query($copy_query)`, instead of waiting for the results in the `relevanssi_hits_filter` filter, but I got the same results, as expected I supposed.

        This leads me to believe that the issue might be deeper into how Relevanssi hooks into WordPress search… and… writing this comment got me thinking that I might be able to find a difference in the blog posts results that could help narrow down the issue.

        One difference that I found is that the Blog post that comes back has the key words in the Title, and the one that is shown in the results but not in the count has the keywords in an indexed custom field. So I guess my copied query is not returning results from indexed custom fields. Does that help?

          1. Mikko, thank you for your insight, it really did help me not to go down the wrong rabbit hole.

            The issue was the “Throttle search” option 🤦‍♂️. On the count query the results that landed > 500 where obviously not counted.

Leave a Reply to John Robertson Nocos Cancel reply

Are you a Relevanssi Premium customer looking for support? Please use the Premium support form.

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.