14

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.

2
  • and the question is? 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. Dec 9, 2011 at 0:53

6 Answers 6

4

If your up for using a plugin, Relevanssi - A Better Search might be worth trying out.

The standard (free) version supports searching post meta.

11

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');
2
  • 10
    This will only match posts that matches the search term in title/content and in the meta field which is not how a full text search typically works.
    – David
    Jul 9, 2017 at 16:16
  • This will be painfully slow as meta values are not indexed.
    – vancoder
    Aug 18, 2020 at 18:31
8

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/

3
  • 1
    The link for adambalee.com worked for me. It just does a general search in all post meta.
    – Joe
    Jan 29, 2019 at 15:09
  • 3
    Likewise, the solution linked to at https://adambalee.com/search-wordpress-by-custom-fields-without-a-plugin/ worked for me on WordPress 5.1.1...
    – jsmod
    Mar 29, 2019 at 15:55
  • did you figure out a way to search only a few custom fields? I have a lots and almost every search is matching some content even if its for the admin only.
    – v3nt
    Jun 16, 2020 at 15:59
8

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).

1
  • Changing the AND to OR breaks the SQL logic for filtering the POST type so the search will apply across all post types, including attachments. Still trying to work out a viable solution that works with search & filters combined on a CPT Nov 2, 2021 at 15:12
2

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.com
 * @link https://meevly.com/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 );
0

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.