Orderby meta_value somente retorna postagens que possuem meta_key existente

10

Eu tenho o seguinte 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 resulta porque existem apenas 10 news posts com meta_key = custom_author_name . Mas há centenas de news postagens que não têm uma linha post_meta com essa meta_key específica. Por favor note que não há meta_query envolvido. Nenhum meta_value é atribuído porque estou apenas tentando classificar as postagens por meta_key e não filtrar por meta_value.

Não deve ordenar por selecionar todas as postagens? e apenas pedi-los?

Se sim, por que o resultado é filtrado? Se o meta_key não for encontrado, por que não usar apenas uma string vazia ou uma correspondência?

Se não, por que não?

Se eu inserir uma meta_key em todos os posts de notícias (mesmo que seja uma string vazia), obtenho o resultado esperado. Mas isso parece um monte de linhas de tabelas que não precisam estar lá.

    
por gdaniel 14.05.2015 / 01:14

4 respostas

9

Como afirmado na resposta de @ ambroseya, supostamente funciona assim. Depois de declarar uma meta-consulta, mesmo que você não esteja procurando um valor específico, ela só consultará as postagens com essa meta-chave declarada. Se você quiser incluir todas as postagens, classifique-as pela meta key, use o seguinte código:

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

$query = new WP_Query($args);

echo $query->found_posts;

O que isso faz é usar uma meta consulta avançada que procura postagens que têm e não têm essa meta-chave declarada. Como o que tem EXISTS é o primeiro, quando você classifica por meta_value , ele usa a primeira consulta.

    
por Manny Fleurmond 14.05.2015 / 04:41
1

Isso é realmente como funciona.

Se você quiser fazer isso sem adicionar linhas de tabela, terá que fazer duas consultas. Um com o meta_key que tem resultados limitados e o outro que obtém toda a lista; Em seguida, use o PHP para comparar os dois resultados da consulta (possivelmente removendo os resultados metakey da outra consulta para remover duplicatas, ou o que for que faça sentido em sua configuração).

    
por ambroseya 14.05.2015 / 03:45
0

Infelizmente, não é assim que WP_Query funciona. Assim que você adicionar esse componente "meta", você criou um tipo de filtro. Despeje $query->request e você verá o que quero dizer.

Segundo, WP_Query não suporta a ordenação por uma meta chave . Você pode ordenar por um meta valor para uma chave particular, mas não pela própria chave. Novamente, despeje a consulta para ver o que quero dizer. Você notará que os componentes "order" serão removidos se você tentar.

A maneira mais limpa de fazer isso funcionar, na minha opinião, é um par de filtros curtos:

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
    
por s_ha_dum 14.05.2015 / 15:41
0

Eu tentei aplicar a resposta do @Manny Fleurmond e, como o @Jake, não consegui fazê-lo funcionar mesmo depois de corrigir o erro de digitação que 'orderby' => 'meta_key' deveria ser 'orderby' => 'meta_value' . (E, para completar, deve ser 'posts_per_page' não 'post_per_page' , mas isso não afeta o problema que está sendo analisado.)

Se você olhar para a consulta SQL realmente gerada pela resposta do @Manny Fleurmond (corrigindo os erros de digitação), isso é o que você obtém:

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

Isso ilustra o modo como o WP está analisando a consulta vars: está criando uma tabela para cada cláusula meta_query e, em seguida, descobre como associá-los e o que pedir. A ordenação funcionaria bem se você estivesse usando apenas uma única cláusula com 'compare' => 'EXISTS' , mas juntando a segunda cláusula 'compare' => 'NOT EXISTS' com OR (como devemos) atrapalha a ordenação. O resultado é que LEFT JOIN é usado para unir a primeira cláusula / tabela e a segunda cláusula / tabela - e a maneira como o WP coloca tudo junto significa que a tabela criada usando 'compare' => 'EXISTS' está sendo preenchida com meta_values de QUALQUER campo personalizado, não apenas o campo 'custom_author_name' em que estamos interessados. Então, acho que ordenar por essa cláusula / tabela só dará os resultados desejados se o post_type específico de 'news' tiver apenas um único campo personalizado.

A solução que funcionou para minha situação foi encomendar pela outra cláusula / tabela - a NOT EXISTS. Aparentemente contra-intuitivo eu sei, mas por causa da maneira como o WP analisa a consulta vars, é essa tabela onde meta_value é preenchida apenas pelo campo personalizado que procuramos.

(A única maneira de descobrir isso foi executando o equivalente a essa consulta para o meu caso:

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

Tudo o que fiz foi alterar as colunas exibidas e remover a cláusula GROUP BY. Isso então me mostrou o que estava acontecendo - que a coluna postmeta.meta_value estava obtendo valores de todas as meta_keys, enquanto a coluna mt1.meta_value estava obtendo apenas meta_values do campo personalizado de notícias.)

A solução

Como @Manny Fleurmond diz, é a primeira cláusula que é usada para o orderby, então a resposta é apenas trocar as cláusulas, dando isto:

$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);

Como alternativa, você pode criar as matrizes associativas de cláusulas e ordenar pela chave correspondente, assim:

$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);
    
por jlad26 05.11.2017 / 10:09