Orderby meta_value仅返回具有现有meta_key的帖子


10

我有以下wp_query:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_key',
    'order' => 'ASC',
    'meta_key'=>'custom_author_name',
    'post_per_page'=>-1
);

$query = new WP_Query($args);

echo $query->found_posts;

echo = 10结果,因为只有10 news个帖子带有meta_key = custom_author_name。但是有数百个news帖子没有包含具有特定meta_key的post_meta行。请注意,不涉及meta_query。没有分配meta_value,因为我仅尝试按meta_key对帖子进行排序,而不按meta_value进行过滤。

不应该按顺序选择所有帖子吗?并订购它们?

如果是这样,为什么要过滤结果?如果找不到meta_key,为什么不使用空字符串或全部匹配呢?

如果没有,为什么不呢?

如果我在每个新闻帖子中都输入meta_key(即使它是一个空字符串),那么我会得到预期的结果。但这似乎是很多表行,不需要在那里。

Answers:


10

如@ambroseya的回答所述,它应该可以那样工作。声明元查询后,即使您不查找特定值,它也只会查询声明了该元键的帖子。如果要包括所有帖子,请按meta键对其进行排序,请使用以下代码:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array( 
            'key'=>'custom_author_name',
            'compare' => 'EXISTS'           
        ),
        array( 
            'key'=>'custom_author_name',
            'compare' => 'NOT EXISTS'           
        )
    ),
    'posts_per_page'=>-1
);

$query = new WP_Query($args);

echo $query->found_posts;

这样做是使用高级元查询,该查询将查找具有和未声明该元键的帖子。由于with EXISTS是第一个,因此当您按排序时meta_value,它将使用第一个查询。


3
这对我来说根本没有改变顺序,当我使用时'orderby' => 'meta_value',它确实改变了顺序,但是与实际的meta字段无关。
杰克

3

我尝试应用@Manny Fleurmond的答案,就像@Jake一样,即使更正了'orderby' => 'meta_key'应该的错字,我也无法使它正常工作'orderby' => 'meta_value'。(出于完整性考虑,应该'posts_per_page'不是,'post_per_page'但这不会影响正在研究的问题。)

如果您查看由@Manny Fleurmond的答案实际生成的SQL查询(已纠正输入错误),那么您将获得:

SELECT   wp_{prefix}_posts.* FROM wp_{prefix}_posts
LEFT JOIN wp_{prefix}_postmeta ON (wp_{prefix}_posts.ID = wp_{prefix}_postmeta.post_id AND wp_{prefix}_postmeta.meta_key = 'custom_author_name' )
LEFT JOIN wp_{prefix}_postmeta AS mt1 ON ( wp_{prefix}_posts.ID = mt1.post_id )
WHERE 1=1  AND ( 
    wp_{prefix}_postmeta.post_id IS NULL 
    OR 
    mt1.meta_key = 'custom_author_name'
) AND wp_{prefix}_posts.post_type = 'news' AND
(wp_{prefix}_posts.post_status = 'publish' OR wp_{prefix}_posts.post_author = 1 AND wp_{prefix}_posts.post_status = 'private')
GROUP BY wp_{prefix}_posts.ID ORDER BY wp_{prefix}_postmeta.meta_value ASC

这说明了WP解析查询var的方式:它为每个meta_query子句创建一个表,然后弄清楚如何联接它们以及如何排序。如果仅使用带有的子句'compare' => 'EXISTS',但是将第二个'compare' => 'NOT EXISTS'子句与OR结合使用(必须如此),则排序工作会很好。结果是使用LEFT JOIN来连接第一个子句/表和第二个子句/表-WP将所有内容放在一起的方式意味着'compare' => 'EXISTS'实际上使用ANY自定义字段中的meta_values填充了使用创建的表,而不仅仅是'custom_author_name'因此,我认为如果“ news”的特定post_type仅具有单个自定义字段,则按该子句/表进行的排序将仅提供所需的结果。

适用于我的情况的解决方案是按另一个子句/表排序-NOT EXISTS一个。我知道这似乎违反直觉,但是由于WP分析查询var的方式,该表meta_value仅由我们所需要的自定义字段填充。

(我发现此问题的唯一方法是针对我的情况运行此查询的等效项:

SELECT   wp_{prefix}_posts.ID, wp_{prefix}_postmeta.meta_value, mt1.meta_value FROM wp_{prefix}_posts
LEFT JOIN wp_{prefix}_postmeta ON (wp_{prefix}_posts.ID = wp_{prefix}_postmeta.post_id AND wp_{prefix}_postmeta.meta_key = 'custom_author_name' )
LEFT JOIN wp_{prefix}_postmeta AS mt1 ON ( wp_{prefix}_posts.ID = mt1.post_id )
WHERE 1=1  AND ( 
    wp_{prefix}_postmeta.post_id IS NULL 
    OR 
    mt1.meta_key = 'custom_author_name'
) AND wp_{prefix}_posts.post_type = 'news' AND
(wp_{prefix}_posts.post_status = 'publish' OR wp_{prefix}_posts.post_author = 1 AND wp_{prefix}_posts.post_status = 'private')
ORDER BY wp_{prefix}_postmeta.meta_value ASC

我所做的只是更改了显示的列并删除了GROUP BY子句。然后,这向我展示了正在发生的情况-postmeta.meta_value列从所有meta_keys中提取值,而mt1.meta_value列仅从新闻自定义字段中提取meta_values。)

解决方案

就像@Manny Fleurmond所说的,这是用于orderby的第一个子句,因此答案只是交换子句,给出以下内容:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array( 
            'key' => 'custom_author_name',
            'compare' => 'NOT EXISTS'           
        ),
        array( 
            'key' => 'custom_author_name',
            'compare' => 'EXISTS'           
        )
    ),
    'posts_per_page' => -1
);

$query = new WP_Query($args);

或者,您可以通过相应的键使子句关联数组和顺序,如下所示:

$args = array(
    'post_type' => 'news',
    'orderby' => 'not_exists_clause',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        'exists_clause' => array( 
            'key' => 'custom_author_name',
            'compare' => 'EXISTS'           
        ),
        'not_exists_clause' => array( 
            'key' => 'custom_author_name',
            'compare' => 'NOT EXISTS'           
        )
    ),
    'posts_per_page' => -1
);

$query = new WP_Query($args);

值得指出的是,如果custom_author_name设置了元键然后又未设置元键,meta_key它将响应,EXISTS结果是这些键将与带有的帖子一起冒泡custom_author_name。就我而言,我有一个复选框,因此我使用"value" => "1"而不是EXISTS,但是字符串将需要使用其他方法。
djb

1

实际上就是这样。

如果要在不添加表行的情况下执行此操作,则必须执行两个查询。一个带有meta_key结果有限,另一个带有整个列表。然后使用PHP比较这两个查询结果(可以从另一个查询中删除meta_key结果以删除重复项,或者在您的设置中有意义的任何方法)。


0

不幸的是,这不是WP_Query工作方式。添加该“元”组件后,就创建了一种过滤器。转储$query->request,您将明白我的意思。

其次,WP_Query根本不支持按meta 排序。您可以按特定键的元排序,但不能按键本身排序。同样,转储查询以了解我的意思。您会发现,如果尝试,“订单”组件将丢失。

我认为,最简单的方法是几个简短的过滤器:

function join_meta_wpse_188287($join) {
  remove_filter('posts_join','join_meta_wpse_188287');
  global $wpdb;
  return ' INNER JOIN '.$wpdb->postmeta.' ON ('.$wpdb->posts.'.ID = '.$wpdb->postmeta.'.post_id)';
}
add_filter('posts_join','join_meta_wpse_188287');

function orderby_meta_wpse_188287($orderby) {
  remove_filter('posts_orderby','orderby_meta_wpse_188287');
  global $wpdb;
  return $wpdb->postmeta.'.meta_key ASC';
}
add_filter('posts_orderby','orderby_meta_wpse_188287');

$args = array(
    'post_type' => 'news',
    'post_per_page'=>-1
);
$q = new WP_Query($args);
var_dump($q->request); // debug
var_dump(wp_list_pluck($q->posts,'post_title')); // debug
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.