使用元查询(‘META_QUERY’)和搜索查询(‘%s’)

时间:2013-01-08 作者:luke

尝试构建一个搜索,不仅搜索默认值(标题、内容等),还搜索特定的自定义字段。

我的当前查询:

$args = array(
  \'post_type\' => \'post\',
  \'s\' => $query,
  \'meta_query\' => array(
     array(
       \'key\' => \'speel\',
       \'value\' => $query,
       \'compare\' => \'LIKE\'
     )
   )
);

$search = new WP_Query( $args )
...
这将返回与搜索查询和元查询都匹配的帖子,但我也希望它也返回只与其中一个匹配的帖子。

有什么想法吗?

13 个回复
最合适的回答,由SO网友:luke 整理而成

根据Nick Perkins\' 根据建议,我必须合并两个查询,如下所示:

$q1 = get_posts(array(
        \'fields\' => \'ids\',
        \'post_type\' => \'post\',
        \'s\' => $query
));

$q2 = get_posts(array(
        \'fields\' => \'ids\',
        \'post_type\' => \'post\',
        \'meta_query\' => array(
            array(
               \'key\' => \'speel\',
               \'value\' => $query,
               \'compare\' => \'LIKE\'
            )
         )
));

$unique = array_unique( array_merge( $q1, $q2 ) );

$posts = get_posts(array(
    \'post_type\' => \'posts\',
    \'post__in\' => $unique,
    \'post_status\' => \'publish\',
    \'posts_per_page\' => -1
));

if( $posts ) : foreach( $posts as $post ) :
     setup_postdata($post);

     // now use standard loop functions like the_title() etc.     

enforeach; endif;

SO网友:Satbir Kira

我已经寻找了几个小时来解决这个问题。数组合并不是一个好办法,尤其是当查询很复杂并且将来必须能够添加到元查询时。解决方案是simplistically beautiful 将“s”改为允许搜索标题和元字段的。

add_action( \'pre_get_posts\', function( $q )
{
    if( $title = $q->get( \'_meta_or_title\' ) )
    {
        add_filter( \'get_meta_sql\', function( $sql ) use ( $title )
        {
            global $wpdb;

            // Only run once:
            static $nr = 0; 
            if( 0 != $nr++ ) return $sql;

            // Modified WHERE
            $sql[\'where\'] = sprintf(
                " AND ( %s OR %s ) ",
                $wpdb->prepare( "{$wpdb->posts}.post_title like \'%%%s%%\'", $title),
                mb_substr( $sql[\'where\'], 5, mb_strlen( $sql[\'where\'] ) )
            );

            return $sql;
        });
    }
});
用法:

$meta_query = array();
$args = array();
$search_string = "test";

$meta_query[] = array(
    \'key\' => \'staff_name\',
    \'value\' => $search_string,
    \'compare\' => \'LIKE\'
);
$meta_query[] = array(
    \'key\' => \'staff_email\',
    \'value\' => $search_string,
    \'compare\' => \'LIKE\'
);

//if there is more than one meta query \'or\' them
if(count($meta_query) > 1) {
    $meta_query[\'relation\'] = \'OR\';
}

// The Query
$args[\'post_type\'] = "staff";
$args[\'_meta_or_title\'] = $search_string; //not using \'s\' anymore
$args[\'meta_query\'] = $meta_query;



$the_query = new WP_Query($args)

SO网友:A.Jesin

通过使用this answer.

$q1 = new WP_Query( array(
    \'post_type\' => \'post\',
    \'posts_per_page\' => -1,
    \'s\' => $query
));

$q2 = new WP_Query( array(
    \'post_type\' => \'post\',
    \'posts_per_page\' => -1,
    \'meta_query\' => array(
        array(
           \'key\' => \'speel\',
           \'value\' => $query,
           \'compare\' => \'LIKE\'
        )
     )
));

$result = new WP_Query();
$result->posts = array_unique( array_merge( $q1->posts, $q2->posts ), SORT_REGULAR );
$result->post_count = count( $result->posts );

SO网友:MarcoO

我也有同样的问题,对于我的新网站,我只是添加了一个新的meta“;标题:

功能。php

add_action(\'save_post\', \'title_to_meta\');

function title_to_meta($post_id)
{
    update_post_meta($post_id, \'title\', get_the_title($post_id)); 
}
然后。。只需添加以下内容:

$sub = array(\'relation\' => \'OR\');

$sub[] = array(
    \'key\'     => \'tags\',
    \'value\'   => $_POST[\'q\'],
    \'compare\' => \'LIKE\',
);

$sub[] = array(
    \'key\'     => \'description\',
    \'value\'   => $_POST[\'q\'],
    \'compare\' => \'LIKE\',
);

$sub[] = array(
    \'key\'     => \'title\',
    \'value\'   => $_POST[\'q\'],
    \'compare\' => \'LIKE\',
);

$params[\'meta_query\'] = $sub;
你认为这种变通方法怎么样?

SO网友:Sebastian Piskorski

我对@Stabir Kira的答案进行了一些优化

function wp78649_extend_search( $query ) {
    $search_term = filter_input( INPUT_GET, \'s\', FILTER_SANITIZE_NUMBER_INT) ?: 0;
    if (
        $query->is_search
        && !is_admin()
        && $query->is_main_query()
        && //your extra condition
    ) {
        $query->set(\'meta_query\', [
            [
                \'key\' => \'meta_key\',
                \'value\' => $search_term,
                \'compare\' => \'=\'
            ]
        ]);

        add_filter( \'get_meta_sql\', function( $sql )
        {
            global $wpdb;

            static $nr = 0;
            if( 0 != $nr++ ) return $sql;

            $sql[\'where\'] = mb_eregi_replace( \'^ AND\', \' OR\', $sql[\'where\']);

            return $sql;
        });
    }
    return $query;
}
add_action( \'pre_get_posts\', \'wp78649_extend_search\');
现在您可以按(标题、内容、排泄物)或(元字段)或(两者)进行搜索。

SO网友:M-R

嗯,这是一种黑客行为,但很有效。您需要添加posts\\u子句过滤器。此筛选函数检查自定义字段“speel”中是否存在任何查询词,其余查询保持不变。

function custom_search_where($pieces) {

    // filter for your query
    if (is_search() && !is_admin()) {

        global $wpdb;

        $keywords = explode(\' \', get_query_var(\'s\'));
        $query = "";
        foreach ($keywords as $word) {

            // skip possible adverbs and numbers
            if (is_numeric($word) || strlen($word) <= 2) 
                continue;

            $query .= "((mypm1.meta_key = \'speel\')";
            $query .= " AND (mypm1.meta_value  LIKE \'%{$word}%\')) OR ";
        }

        if (!empty($query)) {
            // add to where clause
            $pieces[\'where\'] = str_replace("(((wp_posts.post_title LIKE \'%", "( {$query} ((wp_posts.post_title LIKE \'%", $pieces[\'where\']);

            $pieces[\'join\'] = $pieces[\'join\'] . " INNER JOIN {$wpdb->postmeta} AS mypm1 ON ({$wpdb->posts}.ID = mypm1.post_id)";
        }
    }
    return ($pieces);
}
add_filter(\'posts_clauses\', \'custom_search_where\', 20, 1);

SO网友:rAthus

我找不到一个解决方案来查找可以混合在帖子标题、描述和/或一个或多个元中的多个关键字,所以我自己添加了搜索功能。

您只需在函数中添加以下代码。php,每当您在标准的WP\\u Query()函数中使用\'s\'参数并希望它也在一个或多个元字段中搜索时,只需添加一个\'s_meta_keys\' 参数,该参数是要在其中搜索的元键的数组:

/************************************************************************\\
|**                                                                    **|
|**  Allow WP_Query() search function to look for multiple keywords    **|
|**  in metas in addition to post_title and post_content               **|
|**                                                                    **|
|**  By rAthus @ Arkanite                                              **|
|**  Created: 2020-08-18                                               **|
|**  Updated: 2020-08-19                                               **|
|**                                                                    **|
|**  Just use the usual \'s\' argument and add a \'s_meta_keys\' argument  **|
|**  containing an array of the meta(s) key you want to search in :)   **|
|**                                                                    **|
|**  Example :                                                         **|
|**                                                                    **|
|**  $args = array(                                                    **|
|**      \'numberposts\'  => -1,                                         **|
|**      \'post_type\' => \'post\',                                        **|
|**      \'s\' => $MY_SEARCH_STRING,                                     **|
|**      \'s_meta_keys\' => array(\'META_KEY_1\',\'META_KEY_2\');            **|
|**      \'orderby\' => \'date\',                                          **|
|**      \'order\'   => \'DESC\',                                          **|
|**  );                                                                **|
|**  $posts = new WP_Query($args);                                     **|
|**                                                                    **|
\\************************************************************************/
add_action(\'pre_get_posts\', \'my_search_query\'); // add the special search fonction on each get_posts query (this includes WP_Query())
function my_search_query($query) {
    if ($query->is_search() and $query->query_vars and $query->query_vars[\'s\'] and $query->query_vars[\'s_meta_keys\']) { // if we are searching using the \'s\' argument and added a \'s_meta_keys\' argument
        global $wpdb;
        $search = $query->query_vars[\'s\']; // get the search string
        $ids = array(); // initiate array of martching post ids per searched keyword
        foreach (explode(\' \',$search) as $term) { // explode keywords and look for matching results for each
            $term = trim($term); // remove unnecessary spaces
            if (!empty($term)) { // check the the keyword is not empty
                $query_posts = $wpdb->prepare("SELECT * FROM {$wpdb->posts} WHERE post_status=\'publish\' AND ((post_title LIKE \'%%%s%%\') OR (post_content LIKE \'%%%s%%\'))", $term, $term); // search in title and content like the normal function does
                $ids_posts = [];
                $results = $wpdb->get_results($query_posts);
                if ($wpdb->last_error)
                    die($wpdb->last_error);
                foreach ($results as $result)
                    $ids_posts[] = $result->ID; // gather matching post ids
                $query_meta = [];
                foreach($query->query_vars[\'s_meta_keys\'] as $meta_key) // now construct a search query the search in each desired meta key
                    $query_meta[] = $wpdb->prepare("meta_key=\'%s\' AND meta_value LIKE \'%%%s%%\'", $meta_key, $term);
                $query_metas = $wpdb->prepare("SELECT * FROM {$wpdb->postmeta} WHERE ((".implode(\') OR (\',$query_meta)."))");
                $ids_metas = [];
                $results = $wpdb->get_results($query_metas);
                if ($wpdb->last_error)
                    die($wpdb->last_error);
                foreach ($results as $result)
                    $ids_metas[] = $result->post_id; // gather matching post ids
                $merged = array_merge($ids_posts,$ids_metas); // merge the title, content and meta ids resulting from both queries
                $unique = array_unique($merged); // remove duplicates
                if (!$unique)
                    $unique = array(0); // if no result, add a "0" id otherwise all posts wil lbe returned
                $ids[] = $unique; // add array of matching ids into the main array
            }
        }
        if (count($ids)>1)
            $intersected = call_user_func_array(\'array_intersect\',$ids); // if several keywords keep only ids that are found in all keywords\' matching arrays
        else
            $intersected = $ids[0]; // otherwise keep the single matching ids array
        $unique = array_unique($intersected); // remove duplicates
        if (!$unique)
            $unique = array(0); // if no result, add a "0" id otherwise all posts wil lbe returned
        unset($query->query_vars[\'s\']); // unset normal search query
        $query->set(\'post__in\',$unique); // add a filter by post id instead
    }
}
示例使用:

$search= "kewords to search";

$args = array(
    \'numberposts\'   => -1,
    \'post_type\' => \'post\',
    \'s\' => $search,
    \'s_meta_keys\' => array(\'short_desc\',\'tags\');
    \'orderby\' => \'date\',
    \'order\'   => \'DESC\',
);

$posts = new WP_Query($args);
此示例将查找关键字;kewords to search“要搜索的关键字”;在帖子标题、描述和元键“short\\u desc”和“tags”中。

关键字可以在一个或多个文件中找到,按任何顺序,它将返回在任何指定字段中包含所有关键字的任何帖子。

如果希望所有搜索查询都包含这些元键,则可以强制搜索包含在功能中的元键列表,并删除额外的agrument:)

希望这将帮助任何人谁面临同样的问题,我做了!

SO网友:Tim

以上所有解决方案仅在speel元键中存在匹配项时返回结果。如果你在其他地方有结果,但在这个领域没有,你将一无所获。没有人想要那样。

需要左连接。下面将创建一个。

           $meta_query_args = array(
              \'relation\' => \'OR\',
              array(
                \'key\' => \'speel\',
                \'value\' => $search_term,
                \'compare\' => \'LIKE\',
              ),
              array(
                \'key\' => \'speel\',
                \'compare\' => \'NOT EXISTS\',
              ),
            );
            $query->set(\'meta_query\', $meta_query_args);

SO网友:Phong Tran

另一种方法是,只需使用“posts\\u where\\u request”过滤器更改请求。除了(\'s\'和\'meta\\u query\')=>(\'s\'或\'meta\\u query\'),所有内容都将仍然是默认值。

AND ( ((posts.post_title LIKE \'Lily\') OR (posts.post_excerpt LIKE \'Lily\') OR (posts.post_content LIKE \'Lily\')) )
AND ( ( postmeta.meta_key = \'author\' AND postmeta.meta_value LIKE \'Lily\' ) )

=>

AND ( 
    ( ( postmeta.meta_key = \'author\' AND postmeta.meta_value LIKE \'Lily\' ) )
    OR
    ((posts.post_title LIKE \'Lily\') OR (posts.post_excerpt LIKE \'Lily\') OR (posts.post_content LIKE \'Lily\'))
)
这是代码

function edit_request_wp_query( $where ) {
    global $wpdb;
    if ( strpos($where, $wpdb->postmeta.\'.meta_key\') && strpos($where, $wpdb->posts.\'.post_title\') ) {
        $string = $where;
        $index_meta = index_argument_in_request($string, $wpdb->postmeta.\'.meta_key\', $wpdb->postmeta.\'.meta_value\');
        $meta_query = substr($string, $index_meta[\'start\'], $index_meta[\'end\']-$index_meta[\'start\']);
        $string = str_replace( $meta_query, \'\', $string );

        $meta_query = ltrim($meta_query, \'AND\').\' OR \'; 
        $index_s = index_argument_in_request($string, $wpdb->posts.\'.post_title\');
        $insert_to = strpos($string, \'(\', $index_s[\'start\'])+1;
        $string = substr_replace($string, $meta_query, $insert_to, 0);

        $where = $string;
    }
    return $where;
}
add_filter(\'posts_where_request\', \'edit_request_wp_query\');

function index_argument_in_request($string, $key_start, $key_end = \'\') {
    if (!$key_end) $key_end = $key_start;
    $index_key_start = strpos($string, $key_start);
    $string_before = substr($string, 0, $index_key_start);
    $index_start = strrpos($string_before, \'AND\');

    $last_index_key = strrpos($string, $key_end);
    $index_end = strpos($string, \'AND\', $last_index_key);

    return [\'start\' => $index_start, \'end\' => $index_end];
}

SO网友:jpasosa

对于我来说,完美的下一个代码:

            $search_word = $_GET[\'id\'];
        $data[\'words\'] = trim(urldecode($search_word));

        $q1 = new WP_Query( array(
            \'post_type\' => array(\'notas\', \'productos\'),
            \'posts_per_page\' => -1,
            \'s\' => $search_word
        ));

        $q2 = new WP_Query( array(
            \'post_type\' => array(\'notas\', \'productos\'),
            \'posts_per_page\' => -1,
            \'meta_query\' => array(
                \'relation\' => \'OR\',
                array(
                   \'key\'   => \'subtitulo\',
                    \'value\' => $search_word,
                    \'compare\' => \'LIKE\'
                ),
                array(
                    \'key\'   => \'thumbnail_bajada\',
                    \'value\' => $search_word,
                    \'compare\' => \'LIKE\'
                )
             )
        ));

        $result = new WP_Query();
        $result->posts = array_unique( array_merge( $q1->posts, $q2->posts ), SORT_REGULAR );
        $result->post_count = count( $result->posts );

SO网友:Mahdi Akrami

我在WordPress core中找到了一个干净的解决方案。WordPress开发人员在附件中搜索时已遇到此问题_wp_attached_file meta,他们在此函数中修复了此问题:

_filter_query_attachment_filenames()

worpdress run this functiuon

enter image description here

根据此函数的思想,我编写了以下代码来搜索元数据:

   /**
     * Enable Search in postmeta and posts tables in one query
     *
     * @see _filter_query_attachment_filenames()
     */
    add_filter( \'posts_clauses\', function ( $clauses ) {

        global $wpdb;

        // Only run once:
        static $counter = 0;
        if ( 0 != $counter ++ ) {
            return $clauses;
        }

        foreach (
            [
                \'my_custom_meta_1\',
                \'my_custom_meta_2\',
            ] as $index => $meta_key
        ) {

            // Add a LEFT JOIN of the postmeta table so we don\'t trample existing JOINs.
            $clauses[\'join\'] .= " LEFT JOIN {$wpdb->postmeta} AS my_sql{$index} ON ( {$wpdb->posts}.ID = my_sql{$index}.post_id AND my_sql{$index}.meta_key = \'{$meta_key}\' )";

            $clauses[\'where\'] = preg_replace(
                "/\\({$wpdb->posts}.post_content (NOT LIKE|LIKE) (\\\'[^\']+\\\')\\)/",
                "$0 OR ( my_sql{$index}.meta_value $1 $2 )",
                $clauses[\'where\']
            );

        }
        
        return $clauses;
    }, 999 );

SO网友:Gabriel Bustos

这是一个很好的解决方案,但您需要解决一件事。调用“post\\uu-in”时,需要设置一个ID数组,$unique是一个post数组。

示例:

$q1 = get_posts(array(
        \'fields\' => \'ids\',
        \'post_type\' => \'post\',
        \'s\' => $query
));

$q2 = get_posts(array(
        \'fields\' => \'ids\',
        \'post_type\' => \'post\',
        \'meta_query\' => array(
            array(
               \'key\' => \'speel\',
               \'value\' => $query,
               \'compare\' => \'LIKE\'
            )
         )
));

$unique = array_unique( array_merge( $q1->posts, $q2->posts ) );

$array = array(); //here you initialize your array

foreach($posts as $post)
{
    $array[] = $post->ID; //fill the array with post ID
}


$posts = get_posts(array(
    \'post_type\' => \'posts\',
    \'post__in\' => $array,
    \'post_status\' => \'publish\',
    \'posts_per_page\' => -1
));

SO网友:davexpression

@satbir kira answer非常有效,但它只会搜索meta和post标题。如果你想让它搜索元数据、标题和内容,这里是修改后的版本。

    add_action( \'pre_get_posts\', function( $q )
    {
      if( $title = $q->get( \'_meta_or_title\' ) )
      {
        add_filter( \'get_meta_sql\', function( $sql ) use ( $title )
        {
          global $wpdb;

          // Only run once:
          static $nr = 0;
          if( 0 != $nr++ ) return $sql;

          // Modified WHERE
          $sql[\'where\'] = sprintf(
              " AND ( (%s OR %s) OR %s ) ",
              $wpdb->prepare( "{$wpdb->posts}.post_title like \'%%%s%%\'", $title),
              $wpdb->prepare( "{$wpdb->posts}.post_content like \'%%%s%%\'", $title),
              mb_substr( $sql[\'where\'], 5, mb_strlen( $sql[\'where\'] ) )
          );

          return $sql;
        });
      }
    });
下面是它的用法:

$args[\'_meta_or_title\'] = $get[\'search\']; //not using \'s\' anymore

$args[\'meta_query\'] = array(
  \'relation\' => \'OR\',
  array(
    \'key\' => \'_ltc_org_name\',
    \'value\' => $get[\'search\'],
    \'compare\' => \'LIKE\'
  ),
  array(
    \'key\' => \'_ltc_org_school\',
    \'value\' => $get[\'search\'],
    \'compare\' => \'LIKE\'
  ),
  array(
    \'key\' => \'_ltc_district_address\',
    \'value\' => $get[\'search\'],
    \'compare\' => \'LIKE\'
  )
);
更换$get[\'search\'] 使用您的搜索值

结束

相关推荐

Remove some pages from search

在我的网站上,我希望一些页面不能通过搜索表单进行查询(这样当我有类似www.ex.com/?s=banana的内容时,它们就不会出现)有没有一种方法可以从搜索结果页面中“删除”页面(而不是盲目地执行if Is\\u page(id),display:none的条件)