I have a site using Advanced Custom Fields to create and store various bits of relevant info. These need to be included in the main general search, which by default only searches the title and main content.
I've found plenty of info about getting search results based on specific bits of postmeta, but I have an issue with that. There are a potentially unlimited amount of potential fields in postmeta to search.
The solution could either search anything in postmeta or anything with a meta_key that matches this regex: content_section_[0-9]{1,4}_content_.{2,8}
Example meta_keys to match are:
content_section_0_content_title
content_section_0_content_title
content_section_4_content_subtitle
content_section_8_content_text
Any way that I can alter the fields searched to include the postmeta would be very much appreciated.
I have a site using Advanced Custom Fields to create and store various bits of relevant info. These need to be included in the main general search, which by default only searches the title and main content.
I've found plenty of info about getting search results based on specific bits of postmeta, but I have an issue with that. There are a potentially unlimited amount of potential fields in postmeta to search.
The solution could either search anything in postmeta or anything with a meta_key that matches this regex: content_section_[0-9]{1,4}_content_.{2,8}
Example meta_keys to match are:
content_section_0_content_title
content_section_0_content_title
content_section_4_content_subtitle
content_section_8_content_text
Any way that I can alter the fields searched to include the postmeta would be very much appreciated.
Share Improve this question edited Dec 9, 2011 at 0:54 Jodi Warren asked Dec 8, 2011 at 13:51 Jodi WarrenJodi Warren 8162 gold badges10 silver badges17 bronze badges 2- and the question is? – Naoise Golden Commented Dec 8, 2011 at 21:31
- Searching in Wordpress doesn't search data from the post meta - it only searches the title and content fields. I'll update the main question to clarify. – Jodi Warren Commented Dec 9, 2011 at 0:53
6 Answers
Reset to default 6If your up for using a plugin, Relevanssi - A Better Search might be worth trying out.
The standard (free) version supports searching post meta.
Add this to your plugin or functions.php file of your theme. The example below will include 'your_key' in search. You can include all your keys by repeating the array.
function me_search_query( $query ) { if ( $query->is_search ) { $meta_query_args = array( array( 'key' => 'your_key', 'value' => $query->query_vars['s'] = '', 'compare' => 'LIKE', ), ); $query->set('meta_query', $meta_query_args); }; } add_filter( 'pre_get_posts', 'me_search_query');
This function should work, even after WP 4.8.3 security update.
Usage:
// Do this in functions.php
add_meta_field_to_search_query('right_data');
add_meta_field_to_search_query('extra_search_terms');
Implementation:
/* ADD META FIELD TO SEARCH QUERY */
function add_meta_field_to_search_query($field){
if(isset($GLOBALS['added_meta_field_to_search_query'])){
$GLOBALS['added_meta_field_to_search_query'][] = '\'' . $field . '\'';
return;
}
$GLOBALS['added_meta_field_to_search_query'] = array();
$GLOBALS['added_meta_field_to_search_query'][] = '\'' . $field . '\'';
add_filter('posts_join', function($join){
global $wpdb;
if (is_search()){
$join .= " LEFT JOIN $wpdb->postmeta ON $wpdb->posts.ID = $wpdb->postmeta.post_id ";
}
return $join;
});
add_filter('posts_groupby', function($groupby){
global $wpdb;
if (is_search()) {
$groupby = "$wpdb->posts.ID";
}
return $groupby;
});
add_filter('posts_search', function($search_sql){
global $wpdb;
$search_terms = get_query_var('search_terms');
if(!empty($search_terms)){
foreach ($search_terms as $search_term){
$old_or = "OR ({$wpdb->posts}.post_content LIKE '{$wpdb->placeholder_escape()}{$search_term}{$wpdb->placeholder_escape()}')";
$new_or = $old_or . " OR ({$wpdb->postmeta}.meta_value LIKE '{$wpdb->placeholder_escape()}{$search_term}{$wpdb->placeholder_escape()}' AND {$wpdb->postmeta}.meta_key IN (" . implode(', ', $GLOBALS['added_meta_field_to_search_query']) . "))";
$search_sql = str_replace($old_or, $new_or, $search_sql);
}
}
$search_sql = str_replace( " ORDER BY ", " GROUP BY $wpdb->posts.ID ORDER BY ", $search_sql );
return $search_sql;
});
}
Several people have done this in different ways:
http://websmartdesign.nz/searching-structured-post-data-with-wordpress/ https://adambalee/search-wordpress-by-custom-fields-without-a-plugin/
This is an improved Ahmed's answer:
function me_search_query( $query ) {
if ( $query->is_search ) {
$meta_query_args = array(
array(
'key' => 'your_key',
'value' => $query->query_vars['s'],
'compare' => 'LIKE',
),
);
$query->set('meta_query', $meta_query_args);
add_filter( 'get_meta_sql', 'me_replace_and_with_or' );
};
}
function me_replace_and_with_or( $sql ) {
if ( 1 === strpos( $sql['where'], 'AND' ) ) {
$sql['where'] = substr( $sql['where'], 4 );
$sql['where'] = ' OR ' . $sql['where'];
}
//make sure that this filter will fire only once for the meta query
remove_filter( 'get_meta_sql', 'me_replace_and_with_or' );
return $sql;
}
add_filter( 'pre_get_posts', 'me_search_query');
The problem is that WordPress generates meta query with "AND" operator, and it would show only posts which have search string in both places - title AND meta, or content AND meta. So, we need to create an additional filter to change "AND" to "OR" (and then remove that filter to not break anything else).
This is the most simple and currently working solution. None of the above worked for me. It is tested and working with Wordpress 5.3.1 .
Place the following code in your functions.php file to hook into posts_clauses
filter.
/**
* Include meta fields in search
*
* @author Mindaugas // meevly
* @link https://meevly/services/custom-wordpress-themes-and-plugins/
*
* @param array $pieces query pieces.
* @param WP_Query $args query object.
* @return array
*/
function mv_meta_in_search_query( $pieces, $args ) {
global $wpdb;
if ( ! empty( $args->query['s'] ) ) { // only run on search query.
$keywords = explode(' ', get_query_var('s'));
$escaped_percent = $wpdb->placeholder_escape(); // WordPress escapes "%" since 4.8.3 so we can't use percent character directly.
$query = "";
foreach ($keywords as $word) {
$query .= " (unique_postmeta_selector.meta_value LIKE '{$escaped_percent}{$word}{$escaped_percent}') OR ";
}
if ( ! empty( $query ) ) { // append necessary WHERE and JOIN options.
$pieces['where'] = str_replace( "((({$wpdb->posts}.post_title LIKE '{$escaped_percent}", "( {$query} (({$wpdb->posts}.post_title LIKE '{$escaped_percent}", $pieces['where'] );
$pieces['join'] = $pieces['join'] . " INNER JOIN {$wpdb->postmeta} AS unique_postmeta_selector ON ({$wpdb->posts}.ID = unique_postmeta_selector.post_id) ";
}
}
return $pieces;
}
add_filter( 'posts_clauses', 'mv_meta_in_search_query', 20, 2 );
And your query should look like the following. Please note that suppress_filters => false
is mandatory! It will not work without it.
$search_posts_array = array(
'suppress_filters' => false,
's' => $keyword,
);
$search_results = get_posts( $search_posts_array );
I'm not sure, but to grab all the custom field keys/values for the default search would require a database call using wpdb.
The alternative plugin besides the ones goto10 mentioned are a much simpler solution. Search Everything, WP Custom Fields Search