Include category and post tag names in the WordPress search

Include Category and Tags in the WordPress Search

By default, a search in WordPress will only search a post’s title and the post content. Using WordPress filters, it can be extended to include taxonomies, such as categories and tags.

Step #1 – Joining Database Tables

Use posts_join filter to perform an INNER JOIN of the terms, term_relationship, and term_taxonomy tables on the posts table.

add_filter( 'posts_join', 'custom_posts_join', 10, 2 );
/**
 * Callback for WordPress 'posts_join' filter.'
 *
 * @global $wpdb
 *
 * @link https://codex.wordpress.org/Plugin_API/Filter_Reference/posts_join
 *
 * @param string $join The sql JOIN clause.
 * @param WP_Query $wp_query The current WP_Query instance.
 *
 * @return string $join The sql JOIN clause.
 */
function custom_posts_join( $join, $query ) {

    global $wpdb;

    if ( is_main_query() && is_search() ) {

        $join .= "
        LEFT JOIN
        (
            {$wpdb->term_relationships}
            INNER JOIN
                {$wpdb->term_taxonomy} ON {$wpdb->term_taxonomy}.term_taxonomy_id = {$wpdb->term_relationships}.term_taxonomy_id
            INNER JOIN
                {$wpdb->terms} ON {$wpdb->terms}.term_id = {$wpdb->term_taxonomy}.term_id
        )
        ON {$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id ";

    }

    return $join;

}

Step #2 – Append Where Clauses

The posts_where clauses will allow us to append custom where clauses to evaluate the search text on the taxonomy term name field. The included taxonomy names can be narrowed down to category and post_tags.  Wildcards are appended on both sides of the search criteria against the term name.

add_filter( 'posts_where', 'custom_posts_where', 10, 2 );
/**
 * Callback for WordPress 'posts_where' filter.
 *
 * Modify the where clause to include searches against a WordPress taxonomy.
 *
 * @global $wpdb
 *
 * @see https://codex.wordpress.org/Plugin_API/Filter_Reference/posts_where
 *
 * @param string $where The where clause.
 * @param WP_Query $query The current WP_Query.
 *
 * @return string The where clause.
 */
function custom_posts_where( $where, $query ) {

    global $wpdb;

    if ( is_main_query() && is_search() ) {

        // get additional where clause for the user
        $user_where = custom_get_user_posts_where();

        $where .= " OR (
                        {$wpdb->term_taxonomy}.taxonomy IN( 'category', 'post_tag' )
                        AND
                        {$wpdb->terms}.name LIKE '%" . esc_sql( get_query_var( 's' ) ) . "%'
                        {$user_where}
                    )";

    }

    return $where;

}

/**
 * Get a where clause dependent on the current user's status.
 *
 * @global $wpdb https://codex.wordpress.org/Class_Reference/wpdb
 *
 * @uses get_current_user_id()
 * @see http://codex.wordpress.org/Function_Reference/get_current_user_id
 *
 * @return string The user where clause.
 */
function custom_get_user_posts_where() {

    global $wpdb;

    $user_id = get_current_user_id();
    $sql     = '';
    $status  = array( "'publish'" );

    if ( $user_id ) {

        $status[] = "'private'";

        $sql .= " AND {$wpdb->posts}.post_author = {$user_id}";

    }

    $sql .= " AND {$wpdb->posts}.post_status IN( " . implode( ',', $status ) . " ) ";

    return $sql;

}

Step #3 – Grouping The Results

The posts_groupby filter will allow us to condense the results by Post ID. Otherwise, the result would contain many rows with the same Post ID.

add_filter( 'posts_groupby', 'custom_posts_groupby', 10, 2 );
/**
 * Callback for WordPress 'posts_groupby' filter.
 *
 * Set the GROUP BY clause to post IDs.
 *
 * @global $wpdb https://codex.wordpress.org/Class_Reference/wpdb
 *
 * @param string $groupby The GROUPBY caluse.
 * @param WP_Query $query The current WP_Query object.
 *
 * @return string The GROUPBY clause.
 */
function custom_posts_groupby( $groupby, $query ) {

    global $wpdb;

    if ( is_main_query() && is_search() ) {
        $groupby = "{$wpdb->posts}.ID";
    }

    return $groupby;

}

Download The Plugin

I created a plugin called Simple Taxonomy Search on Github. The new plugin contains tests to help me keep up with WordPress updates to ensure it works well. The old plugin repository will eventually be removed.

Simple Taxonomy Search Build Status

Published by

rfmeier

Product developer at Rainmaker Digital and runner from Hampshire, Illinois. I work with php a lot; Spending most of my time digging into source code to see how it all works. You can find me on Twitter @rfmeier.

24 thoughts on “Include Category and Tags in the WordPress Search”

  1. Is there any chance that this can also search 2 or more words? Example I have a post tagged Simple, Clean – so if I input simple clean it will display any post with Simple and Clean tags… or even combined with a category

    1. Chris,

      The way the WordPress search functions is by appending a wildcard on each side. So by searching ‘simple clean’, part of the WHERE clause would look like %simple clean%. So the search would be looking for a match that contains ‘simple clean’.

      Off the top of my head, I can think of two ways around this issue. Use mysql full-text search, which would require altering the database engine. I am not sure what side effects this would produce, but the speed and accuracy would be pretty good.

      Second, you could split the string by an empty space and create multiple wild card searches for each word in the string. The performance would probably be decent with a smaller site, but a large site would probably be slow.

  2. Hi Ryan
    Thanks for this beautiful piece of code.

    I’m currently building a site where I use custom post types to create products and their categories as implemented here. I was wondering if there is a way to search in the categories of the products as well.

    Thanks,
    Nick Pasadakis

  3. Hello,

    Nice snippet! Just a little problem; The search result with this code includes posts that are not published and drafts. Can these be excluded?

    Thank you.

    1. hedi,

      I updated to code to reflect the post status and author status (if you are logged in) when the sql analyzes the taxonomy coniditon.

      This will exclude drafts, but include private posts if the author is logged in.

  4. Hi Ryan,
    I am actually new to wordPress so I don’t have that much idea, where shall I put this codes, in search.php of my theme or query.php.

    Please help
    Thanks

  5. continuing from the above comment

    This is the code i am using to search ….

    $args = array(
        's'         => $_GET['s'],
        'showposts' => -1,
        'post_type' => $searchable_types,// define searchable Post Types
    );
    $tp_allsearch = new WP_Query($args);
    

    So how can I use your functions with the above queries…

    Thanks and Regards

    1. Dev,

      The functionality I provided above are WordPress action and filter callbacks. They will automatically hook into WP_Query that is a search AND the main query. You would have to remove the check for is_main_query() to allow a custom WP_Query to get through.

  6. Your Plugin worked for ‘Tag’ values……..Thanks a lot.
    I have some meta field provided by a Plugin inside ‘create a Post’. Can I include those Meta field as well in my search.
    Regards and once again ….Thanks

  7. Good evening. I am trying to insert your code in my WordPress site, but I cannot get it to work. I imagine I am doing something wrong, but i cannot figure it out. I even installed your plugin, but still does not work for me.

    My theme functions has only this, and it says it uses WordPress search functionality;

    function tie_Search_Filter($query) {
        if( $query->is_search ){
            if ( tie_get_option( 'search_exclude_pages' ) && !is_admin() ){
                $post_types = get_post_types(array( 'public' => true, 'exclude_from_search' => false ));
                unset($post_types['page']);
                $query->set('post_type', $post_types );
            }
            if ( tie_get_option( 'search_cats' ))
                $query->set( 'cat', tie_get_option( 'search_cats' ) && !is_admin() );
        }
        return $query;
    }
    add_filter('pre_get_posts','tie_Search_Filter');
    
    1. George,

      Try commenting out or temporarily removing the code above in your theme and trying to search again. I am assuming they are conflicting.

  8. Hello

    I am trying to get this to work with AJAX searching and custom posts, but using the standard tags taxonomy.

    I cant seem to get it to work. I have removed the ‘main_query’ section of the if statement, but I can’t get it to recognise my tags.

    Any ideas?

    Regards

    Andy

    1. Andy,

      A custom WP_Query will require additional work to allow the filters to know when the query can be altered. The filters are used because that is the only (safe) way to alter the query if a search is performed.

      If I were making a custom search via AJAX (or any custom search), I would take the raw SQL query approach. In my opinion it would be easier to understand. WordPress’s WP_Query is not the most efficient and has a lot of restraints.

      Good luck and sorry about the huge delay in response.

  9. I’ve tried your code on the newest WordPress Release and this does not work at all. It just continues to only search within the post title and post content. I know the code is being ran, but I’m assuming that wordpress is filtering out the taxonomy stuff somehow and only processing the query as normal. Not sure how to fix this, or if this is outdated code.

    1. Solomon,

      Thank you for reading.

      I have tested this searching tags, categories, post title, and post content with successful results on WordPress 4.8.3. Have to attempted to look at the actual SQL being ran? Could something else be override this with another hook? Is this on a production site?

Leave a Reply

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