问题描述

我认为问题本质上与 sql 查询结构有关,我不是专家….

我需要通过 2 个参数搜索帖子 (自定义帖子类型):

  1. pd_city

  2. pd_country

请注意,meta_query 关系是’OR’,所以如果上述两个之一都是有一些结果。

第三键 (is_sponsored) 用于排序帖子! “is_sponsored” 值等于 1 的帖子可以是 1 或 0,而顶部应列出。

所以这里是 WordPress 的事情:

    $sfp_query_args = array(
        'sfp_complex_search' => 'yeap', 
        'tax_query' => array( array( 'taxonomy' => 'sfp_post_category', 'terms' => $term_id ) ),
        //'meta_key' => 'is_sponsored',
        'post_type' => 'sfpposts',
        'post_status' => 'publish',
        'showposts' => (int)$per_page,
        'paged' => $paged, 
        'meta_query' => array( 'relation' => 'OR', 
                array( 'key' => 'pd_city', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), 
                array( 'key' => 'pd_country', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), 
                array( 'key' => 'is_sponsored' )
                )
    );
$sfp_search = new WP_Query( $sfp_query_args );

我也需要用”posts_orderby” 过滤结果,以获得那些赞助到顶部:

add_filter( 'posts_orderby', 'sfp_modify_search' );
function sfp_modify_search( $orderby ) {
    if( !is_admin() && is_page( $this->options[ 'sfp_page_entries_search' ] ) ) {
        global $wpdb;
        $orderby = " CASE WHEN mt2.meta_value = 0 THEN 1 END, $wpdb->posts.post_date DESC ";
    }
    return $orderby;
}

真正的问题依赖于这个查询,返回来自”sfp_post_category” 的所有 POSTS,不仅是匹配”pd_city” 或”pd_country” 的所有 POSTS,因为所有 POSTS 都有”is_sponsored” 元键 (并且值设置为 1 或 0) 。再次:”is_sponsored” 需要排序!

当 var_dump

var_dump( $sfp_search->request );

… WordPress 的 sql 看起来像这样:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
WHERE 1=1 
AND ( wp_term_relationships.term_taxonomy_id IN (77) ) 
AND wp_posts.post_type = 'sfpposts' 
AND (wp_posts.post_status = 'publish') 
AND ( (wp_postmeta.meta_key = 'pd_city' 
AND CAST(wp_postmeta.meta_value AS CHAR) 
LIKE '%something%') 
OR (mt1.meta_key = 'pd_country' 
AND CAST(mt1.meta_value AS CHAR) 
LIKE '%something%') 
OR mt2.meta_key = 'is_sponsored' ) 
GROUP BY wp_posts.ID 
ORDER BY CASE WHEN mt2.meta_value = 0 THEN 1 END, wp_posts.post_date DESC 
LIMIT 0, 10

如何从结果中删除与”pd_city” 或”pd_country” 不匹配的所有帖子?

最佳解决方案

罪魁祸首

这个问题的罪魁祸首是不支持不同和/或嵌套关系的元查询 – 顺便说一下,这也使我以前坚持不了。在最近有一个搜索场景的情况下。

您想要做的只是无法通过 WP_Query 以及一个循环单独完成。如您所知,无论是将排序键放在 meta_query 数组中还是将其作为一般查询参数,都不会有所作为。如果您将元查询关系设置为 OR,并在查询 args 的任何位置指定 meta_key,而不设置附带的 meta_value 参数,查询将始终至少返回设置该 meta_key 的所有帖子。为方便起见,为了完整性:当使用具有!=的单个 meta_query 作为 meta_compare 的值时,查询将返回所有结果,而 meta_key 设置不等于给定的 meta_value – 它不会返回任何没有使用 meta_key 的帖子。元查询失败的另一点。

解决方案 1

我看到两个选项。一方面,您可以从查询中省略 is_sponsored 元密钥,省略分页,获取正确的帖子,并使用 WP_Query 的第二个实例进行排序,并通过 post__in 参数传递过滤的帖子 ID:

$sfp_search_args = array(
    'sfp_complex_search' => 'yeap', 
    'tax_query' => array( array( 'taxonomy' => 'sfp_post_category', 'terms' => $term_id ) ),
    'post_type' => 'sfpposts',
    'post_status' => 'publish',
    'meta_query' => array(
        'relation' => 'OR', 
        array( 'key' => 'pd_city', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), 
        array( 'key' => 'pd_country', 'value' => $sfp_search_meta, 'compare' => 'LIKE' )
    )
);

$sfp_search = new WP_Query( $sfp_search_args );
$post_ids = array();
while ( $sfp_search->have_posts() ) : $sfp_search->next_post();
    $post_ids[] = $sfp_search->post->ID;
endwhile;

$sfp_ordered_args(
    'post__in' => $post_ids,
    // note that 'showposts' is deprected
    'posts_per_page' => (int)$per_page, 
    'paged' => $paged,
    'meta_key' => 'is_sponsored',
    'order' => 'DESC',
    'orderby' => 'meta_value_num date'
);
$sfp_ordered = new WP_Query( $sfp_ordered_args );
while ( $sfp_ordered->have_posts() ) : $sfp_ordered->next_post();
    // display posts
endwhile;

请注意,WP_Query$orderby 参数将采用由空格分隔的多个值。您的搜索修改可能比必要更复杂。

解决方案 2

由于我喜欢你的想法 var_dumping 查询对象的 request 属性,让我开始一个快速的,并且,未经测试 – 备注的建议:

如果您通过将 OR mt2.meta_key = 'is_sponsored'的逻辑运算符更改为 AND 稍微修改给定的 SQL,并相应地移动给定的 SQL,则可以使用 $wpdb 来拉动帖子:

$sfp_post_ids = $wpdb->get_col(
    "
    SELECT wp_posts.ID 
    FROM wp_posts 
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
    INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
    INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
    INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
    WHERE 1=1 
    AND ( wp_term_relationships.term_taxonomy_id = $term_id ) 
    AND wp_posts.post_type = 'sfpposts' 
    AND (wp_posts.post_status = 'publish') 
    AND ( (wp_postmeta.meta_key = 'pd_city' 
    AND CAST(wp_postmeta.meta_value AS CHAR) 
    LIKE '%$sfp_search_meta%') 
    OR (mt1.meta_key = 'pd_country' 
    AND CAST(mt1.meta_value AS CHAR) 
    LIKE '%$sfp_search_meta%') )
    AND mt2.meta_key = 'is_sponsored' 
    GROUP BY wp_posts.ID 
    ORDER BY CASE WHEN mt2.meta_value = 0 THEN 1 END, wp_posts.post_date DESC
    "
);

此时,您还有两个选择:使用简单的 foreach$sfp_post_ids 阵列进行迭代,并在该循环中单独使用 get_post()提取后置数据,或者,如果您想要 WP_Query 的优点 – 分页,模板标签等 – 将 $sfp_post_ids 进给到 post__in 参数,如解决方案 1 所示。

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。