Leave a Reply

6 comments

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

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