technology and webhosting news

By default WordPress search works by searching post content and titles. If you have an advanced website where data is store in custom fields, you may want to also search those values. In this guide I will provide you with the code needed to update WordPress so it can search custom fields. All without the need for a 3rd party plugin.

If you are not a developer or are scared to add custom code to your website we would recommend using a 3rd party plugin such as SearchWP or Relevanssi. Both of those plugins are completely free but they do offer premium upgrades.

Cautionary note: The code provided in this tutorial will make it so WordPress will use ALL custom field values in the search calculation. Depending on your site this could create a security concern or slow down your search queries.

Table of contents

Why Search Custom Fields

We wrote an article a while back on Why & How to Improve the Internal WordPress Site Search. The article goes over the reasons why you may want to improve the WordPress site search and which plugins are good for the job. So rather then re-iterating everything here, go check out that post.

That said, an example may be a website that has a post type for the organization’s staff members. For your staff members you will likely have custom fields to store data such as their job title, skills, education, etc. So, you may want to include these fields in the WordPress search calculation to make it easier to locate members.

But before modifying how the WordPress search works, take a second to think if you really need to. There are situations where modifying your search results won’t really provide the best user experience. It may be better to create an AJAX filter so users can select values from various fields to limit the posts by.

How to Search by Custom Fields without a Plugin

In order to allow custom field values to be included in WordPress search results we will need to hook into three different filters (one optional). We’ll filter the JOIN, WHERE and DISTINCT clauses for the search query. I’ll walk you through each filter and explain what it’s doing.

Step 1: Filter the JOIN Clause

We’ll start by modifying the JOIN clause via the posts_join filter.

/**
 * Adds the postmeta table to the search query.
 *
 * @link https://www.wpexplorer.com/how-to-include-custom-field-values-in-wordpress-search/
 */
function wpexplorer_search_posts_join( $join, $query ) {
	if ( $query->is_search() ) {
		global $wpdb;
		$join .= " LEFT JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id";
	}
	return $join;
}
add_filter( 'posts_join', 'wpexplorer_search_posts_join', 10, 2 );

By default, WordPress is set up to search only the “posts” table and since custom fields are stored inside their own “postsmeta” table we’ll need to include it in the Query. That’s what the previous snippet does.

Step: 2 Filter the WHERE Clause

Next we’ll filter the WHERE clause by hooking into posts_where hook.

/**
 * Adds meta values to the search query.
 *
 * @link https://www.wpexplorer.com/how-to-include-custom-field-values-in-wordpress-search/
 */
function wpexplorer_search_posts_where( $where, $query ) {
    if ( $query->is_search() ) {
		global $wpdb;
		$where = preg_replace(
			"/(s*{$wpdb->posts}.post_titles+LIKEs*('[^']+')s*)/",
			"({$wpdb->posts}.post_title LIKE $1) OR ({$wpdb->postmeta}.meta_value LIKE $1)",
			$where
		);
    }
    return $where;
}
add_filter( 'posts_where', 'wpexplorer_search_posts_where', 10, 2 );

The previous code tells the WordPress search query to look inside the meta_value columns. Again, this will include all custom fields. If you only want WordPress to search specific fields the code will be much more complex.

Step 3: Filter the DISTINC Clause (optional)

Last we’ll filter the posts_distinct hook.

/**
 * Prevent duplicate posts in search results.
 *
 * @link https://www.wpexplorer.com/how-to-include-custom-field-values-in-wordpress-search/
 */
function wpexplorer_search_posts_distinct( $where, $query ) {
	if ( $query->is_search() ) {
		return "DISTINCT";
	}
	return $where;
}
add_filter( 'posts_distinct', 'wpexplorer_search_posts_distinct', 10, 2 );

This last bit of code prevents duplicate search results if you have custom fields with the same values added to the same post in different fields. This isn’t generally an issue, but it’s worth mentioning in case. It doesn’t really hurt to add the code regardless (at least I don’t think so).

PHP Class & Plugin

To make it easier, I’ve compiled all 3 snippets above into a single class you can add to your site. Using a class will keep the code separate and neatly organized. You can add the code to your child theme functions.php file or a code snippet plugin.

I recommend adding the PHP class inside it’s own file in your child theme and loading it using require. This will keep your code nicely organized instead of having a massive functions.php.

/**
 * Allow searching by custom fields.
 *
 * @link https://www.wpexplorer.com/how-to-include-custom-field-values-in-wordpress-search/
 */
final class Search_By_Custom_Fields {

	/**
	 * Class constructor.
	 */
	public function __construct() {
		add_filter( 'posts_join', [ $this, 'filter_posts_join' ], 10, 2 );
		add_filter( 'posts_where', [ $this, 'filter_posts_where' ], 10, 2 );
		add_filter( 'posts_distinct', [ $this, 'filter_posts_distinct' ], 10, 2 );
	}

	/**
	 * Adds the postmeta table to the search query.
	 */
	public function filter_posts_join( $join, $query ) {
		if ( $query->is_search() ) {
			global $wpdb;
			$join .= " LEFT JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id";
		}
		return $join;
	}

	/**
	 * Adds meta values to the search query.
	 */
	public function filter_posts_where( $where, $query ) {
		if ( $query->is_search() ) {
			global $wpdb;
			$where = preg_replace(
				"/(s*{$wpdb->posts}.post_titles+LIKEs*('[^']+')s*)/",
				"({$wpdb->posts}.post_title LIKE $1) OR ({$wpdb->postmeta}.meta_value LIKE $1)",
				$where
			);
		}
		return $where;
	}

	/**
	 * Prevent duplicate posts in search results.
	 */
	public function filter_posts_distinct( $where, $query ) {
		if ( $query->is_search() ) {
			return "DISTINCT";
		}
		return $where;
	}

}
new Search_By_Custom_Fields();

Download the Plugin

I’ve also added the code above to Github so you can download it as a plugin. This plugin won’t be uploaded to the WordPress repository so it will never get updates. When you download it you can modify the folder name and plugin details to whatever you want.

I prefer creating mini plugins for code like this. By having the code inside a plugin it makes it easier to troubleshoot site issues since you can quickly disable snippets from your site. I actually have over 50 mini plugins on wpexplorer.com that accomplish various tasks.

Conclusion

As you can see including custom field values in the internal WordPress search is easy. I can definitely see some situations where it may be useful. Let me know if you have any issues or questions regarding the code.

Also, I would be curious to see some real world examples of websites using custom fields in their search results to improve user experience. Please share them in the comments!

Further Reading

While you’re here, you may want to read some related tutorials:

Similar Posts