对PRE_GET_POST使用META_QUERY排除特定的META_KEY值

时间:2014-01-09 作者:Brian Richards

我在首页的主查询上运行了一个简单的过滤器,它应该排除所有具有特定meta值的帖子(只包括没有meta\\u键集的帖子,或者meta\\u键与我们排除的内容不匹配的帖子)。但是,无论出于何种原因,它的表现都不如预期。

/**
 * Hide posts from main query on front page.
 *
 * @since  1.0.0
 *
 * @param  object $query WP Query object.
 * @return object        Modified WP Query object.
 */
function wpse_exclude_posts_from_main_query( $query ) {

    // Make sure this only runs on the main query on the front page
    if ( is_front_page() && $query->is_main_query() ) {

        // Exclude posts that have been explicitly set to hidden
        $query->set(\'meta_query\', array(
            \'relation\' => \'OR\',
            // Include posts where the meta key isn\'t set
            array(
                \'key\'     => \'_wpse_custom_key\',
                \'value\'   => \'asdf\', // A value must exist due to https://core.trac.wordpress.org/ticket/23268
                \'compare\' => \'NOT EXISTS\',
            ),
            // Include posts where the meta key isn\'t explicitly true
            array(
                \'key\'     => \'_wpse_custom_key\',
                \'value\'   => true,
                \'compare\' => \'!=\',
            ),
        ) );

    }

}
add_action( \'pre_get_posts\', \'wpse_exclude_posts_from_main_query\' );
这个元查询的每一半都可以独立工作。我可以看到所有没有关键字的帖子,或者所有有关键字但不正确的帖子。当结合使用时,如上所示,我只看到键确实存在且不为true的帖子(不存在部分被完全忽略)。

下面是正在生成的SQL(根据posts\\u请求过滤器):

SELECT     SQL_CALC_FOUND_ROWS wp_posts.*
FROM       wp_posts
LEFT JOIN  wp_postmeta
           ON (
               wp_posts.ID = wp_postmeta.post_id
               AND wp_postmeta.meta_key = \'_wpse_custom_key\'
           )
INNER JOIN wp_postmeta AS mt1
           ON (wp_posts.ID = mt1.post_id)
WHERE      1=1
           AND wp_posts.post_type = \'post\'
           AND wp_posts.post_status = \'publish\'
           AND (
               wp_postmeta.post_id IS NULL
               OR (
                   mt1.meta_key = \'_wpse_custom_key\'
                   AND CAST(mt1.meta_value AS CHAR) != \'1\'
               )
           )
GROUP BY   wp_posts.ID
ORDER BY   wp_posts.post_date DESC
LIMIT      0, 10
您可以看到,当涉及到元片段时,它确实在使用或在WHERE子句中,而不是像引用的bug所强调的那样使用和:https://core.trac.wordpress.org/ticket/23268

希望有人能提供一些急需的见解,因为我完全不知所措。

3 个回复
SO网友:Rarst

是的,它的行为很奇怪。不,开枪吧,我无法确定确切的原因和方式(可能是堆叠连接)。

我说把它翻过来。你唯一不想看到的帖子是那些true 自定义密钥。分别查询其ID,将结果馈送到post__not_in, 完全抛弃这个元查询。

SO网友:Brian Richards

我支持推特上那些怀疑复合SQL是问题所在的人。我刚刚编写了一个不同的函数来在posts\\u子句中运行我自己的SQL,而不是使用pre\\u get\\u posts,hook和预期的posts会正确返回。

下面是一个与上述要求相同的示例:

/**
 * Hide posts from main query on front page.
 *
 * @since  1.0.0
 *
 * @param  object $query WP Query object.
 * @return object        Modified WP Query object.
 */
function wpse_exclude_posts_clauses( $pieces, $query ) {

    // Make sure this only runs on the main query on the front page
    if ( is_front_page() && $query->is_main_query() ) {
        $pieces[\'join\'] = "
            LEFT JOIN  $wpdb->postmeta as hidden_meta
                       ON (
                           $wpdb->posts.ID = hidden_meta.post_id
                           AND hidden_meta.meta_key = \'_wpse_custom_key\'
                       )
            ";
        $pieces[\'where\'] = "
            AND (
                hidden_meta.post_id IS NULL
                OR CAST(hidden_meta.meta_value AS CHAR) != \'1\'
            )
            ";
    }

    return $pieces;
}
add_filter( \'posts_clauses\', \'wpse_exclude_posts_clauses\', 10, 2 );

SO网友:Chris_O

我认为您的问题是使用true boolean作为要排除的值。我将exclude值更改为字符串“asdf”,并更改了伪字符串,结果成功了。

查询SQL是从全局$wp\\u查询对象的转储中复制和粘贴的。从第一次查询中可以看出,该数据库有5篇文章。其中3个贴子具有排除元键,其中1个包含排除元值。

mysql> SELECT ID FROM wpomni_posts WHERE post_type =\'post\' AND post_status = \'publish\';
+------+
| ID   |
+------+
|    1 |
| 5900 |
| 5904 |
| 5908 |
| 5925 |
+------+
5 rows in set (0.00 sec)
    mysql> SELECT SQL_CALC_FOUND_ROWS  wpomni_posts.ID FROM wpomni_posts  
        -> LEFT JOIN wpomni_postmeta 
        -> ON (wpomni_posts.ID = wpomni_postmeta.post_id 
        -> AND wpomni_postmeta.meta_key = \'_wpse_custom_key\')
        -> INNER JOIN wpomni_postmeta AS mt1 
        -> ON (wpomni_posts.ID = mt1.post_id) 
        -> WHERE 1=1  
        -> AND wpomni_posts.post_type = \'post\' 
        -> AND (wpomni_posts.post_status = \'publish\' 
        -> OR wpomni_posts.post_status = \'private\') 
        -> AND ( wpomni_postmeta.post_id IS NULL
        -> OR  (mt1.meta_key = \'_wpse_custom_key\' 
        -> AND CAST(mt1.meta_value AS CHAR) != \'asdf\') ) 
        -> GROUP BY wpomni_posts.ID 
        -> ORDER BY wpomni_posts.post_date DESC LIMIT 0, 18;
    +------+
    | ID   |
    +------+
    | 5925 |
    | 5908 |
    | 5900 |
    |    1 |
    +------+
    4 rows in set (0.00 sec)

    mysql> SELECT * FROM wpomni_postmeta WHERE meta_key = \'_wpse_custom_key\';
    +---------+---------+------------------+----------------+
    | meta_id | post_id | meta_key         | meta_value     |
    +---------+---------+------------------+----------------+
    |  137033 |    5908 | _wpse_custom_key | 1              |
    |  137034 |    5904 | _wpse_custom_key | asdf           |
    |  137042 |    5925 | _wpse_custom_key | what the blank |
    +---------+---------+------------------+----------------+
    3 rows in set (0.00 sec)

结束

相关推荐