Wordpress - How to make search include data from wp_postmeta?
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.com/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).