具有3个键的复数元查询

时间:2012-10-12 作者:Dameer

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

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

pd\\U市

pd\\U国家

请注意,meta\\u查询关系是“或”,所以如果上面两个中的任何一个是类似的,我们应该有一些结果。

第三个键(is\\U赞助)用于对帖子进行排序!它可以是1或0,“is\\u赞助商”值等于1的帖子应列在顶部。

下面是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\\u 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\\U post\\u category”中的所有帖子,而不仅仅是那些与“pd\\u city”或“pd\\u country”匹配的帖子,因为所有帖子都有“is\\u-Conspondered”元键(值设置为1或0)。再次强调:“is\\U赞助”用于排序!

当var\\u转储时

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\\U城市”或“pd\\U国家”不匹配的帖子?

3 个回复
最合适的回答,由SO网友:Johannes Pille 整理而成

罪魁祸首是元查询不支持不同和/或嵌套关系,顺便说一下,这是一个缺点,以前也让我发疯了。在最近的一个实例中,还有一个搜索场景。

你想做的事根本无法用WP_Query 以及一个回路
您似乎已经注意到,是否将排序键放置在meta_query 数组或其外部作为常规查询参数并没有什么区别。如果将元查询关系设置为OR 并指定meta_key 查询参数的任意位置,而不设置伴随的meta_value 参数,查询将始终至少返回设置了meta\\u键的所有帖子
顺便提一下,为了完整性:当您使用单个meta\\u查询时!= 作为的值meta_compare, 查询将返回所有结果meta_key 设置且不等于给定值meta_value - 它不会返回任何没有meta_key 根本不用。元查询失败的另一点。

解决方案1我看到两种选择。首先,您可以省略is_sponsored 从查询中删除meta键,同时省略分页,获取正确的帖子并使用第二个实例进行排序WP_Query, 通过post__in 参数:

$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;
请注意$orderby 参数WP_Query 将采用由空格分隔的多个值。您的搜索修改可能过于复杂。

解决方案2,因为我喜欢您的var\\u转储查询对象request 属性,让我快速提出一个未经测试的次要建议:

如果通过更改OR mt2.meta_key = \'is_sponsored\'AND 相应地移动它,你可以用$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
    "
);
此时,您还有两个选项:
或者迭代$sfp_post_ids 具有简单foreach 并使用get_post() 在该循环中单独进行,或者,如果您希望WP_Query - 分页、模板标记等-提要$sfp_post_idspost__in 参数如解决方案1所示。

SO网友:Parham

这一切都是因为OR 上的关系meta_query 以及WordPress生成实际查询字符串的方式。我最后迷上了posts_clauses 过滤器以修改whereorderby 查询片段:

public function wpse_68002_orderby_fix($pieces){
    global $wpdb;
    $pieces[\'where\']  .= " AND $wpdb->postmeta.meta_key = \'your_meta_key\'"; // <--- update here with your meta_key name
    $pieces[\'orderby\']  = "$wpdb->postmeta.meta_value ASC";
    return $pieces;
}
只需在设置WP\\U查询对象之前添加过滤器,然后确保在运行查询后将其删除,以免影响其他查询:

    add_filter( \'posts_clauses\', \'wpse_68002_orderby_fix\', 20, 1 );
    $query = new WP_Query($args);
    $result = $query->get_posts();
    remove_filter( \'posts_clauses\', \'wpse_68002_orderby_fix\', 20 );
请记住省去meta_keyorderby 从查询参数。

SO网友:s_ha_dum

这很复杂:)

我想建议你可能不需要array( \'key\' => \'is_sponsored\' ) “meta\\u query”数组中的值,您可以通过向主数组添加“meta\\u键”来实现这一点,但看起来您已经尝试过了。你得到同样的结果了吗?

JOINs会使事情变得复杂。你迷上了posts_orderby. 你有没有想过posts_fields 并添加一个子查询来获取meta\\u值?

(SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = \'is_sponsored\' AND post_id = {$wpdb->posts}.ID) as is_sponsored

结束