Orderby meta_value solo devuelve las publicaciones que tienen meta_key existente

10

Tengo la siguiente 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 resultados porque solo hay un 10% de publicaciones de news con meta_key = custom_author_name . Pero hay cientos de publicaciones news que no tienen una fila post_meta con esa meta_key específica. Por favor, tenga en cuenta, que no hay meta_query involucrado. No se asigna ningún meta_valor, porque solo intento ordenar las publicaciones por meta_key y no filtrar por meta_valor.

¿No debería ordenar seleccionando todas las publicaciones? y solo ordenarlos?

Si es así, ¿por qué se filtra el resultado? Si no se encuentra la meta_key, ¿por qué no usar una cadena vacía o unir todas?

Si no, ¿por qué no?

Si ingreso una meta_key en cada publicación de noticias (incluso si es una cadena vacía) obtengo el resultado esperado. Pero eso parece una gran cantidad de filas de tablas que no necesitan estar allí.

    
pregunta gdaniel 14.05.2015 - 01:14

4 respuestas

9

Como se indica en la respuesta de @ambroseya, se supone que funciona así. Una vez que declare una meta consulta, incluso si no está buscando un valor específico, solo consultará las publicaciones con esa meta clave declarada. Si desea incluir todas las publicaciones, ordénelas por la meta clave, use el siguiente 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;

Lo que esto hace es usar una meta consulta avanzada que busca las publicaciones que tienen y no tienen esa clave meta declarada. Como el primero con EXISTS es el primero, cuando ordenes por meta_value , utilizará la primera consulta.

    
respondido por el Manny Fleurmond 14.05.2015 - 04:41
1

Así es como funciona.

Si desea hacerlo sin agregar filas a la tabla, tendrá que hacer dos consultas. Uno con la meta_key que tiene los resultados limitados, y el otro que obtiene la lista completa; luego use PHP para comparar los dos resultados de la consulta (posiblemente eliminar los resultados de meta_key de la otra consulta para eliminar los duplicados, o lo que tenga sentido en su configuración).

    
respondido por el ambroseya 14.05.2015 - 03:45
0

Lamentablemente, no es así como funciona WP_Query . Tan pronto como agregas ese componente "meta", has creado un tipo de filtro. Dump $query->request y verás lo que quiero decir.

En segundo lugar, WP_Query no admite el pedido con una meta clave en absoluto. Puede ordenar por un meta valor para una clave en particular, pero no por la propia clave. Una vez más, volcar la consulta para ver lo que quiero decir. Notarás que los componentes de "orden" se retiran si lo intentas.

La forma más limpia de hacer que esto funcione, en mi opinión, es un par de filtros cortos:

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

Intenté aplicar la respuesta de @Manny Fleurmond y, al igual que @Jake, no pude hacer que funcionara incluso después de corregir el error tipográfico que 'orderby' => 'meta_key' debería ser 'orderby' => 'meta_value' . (Y para completar, debe ser 'posts_per_page' no 'post_per_page' , pero eso no afecta el problema que se está analizando).

Si observas la consulta SQL generada realmente por la respuesta de @Manny Fleurmond (habiendo corregido los errores tipográficos) esto es lo que obtienes:

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

Esto ilustra la forma en que WP está analizando las variables de consulta: está creando una tabla para cada cláusula meta_query, y luego averigua cómo unirlas y qué ordenar. El ordenamiento funcionaría bien si solo estuviera usando una única cláusula con 'compare' => 'EXISTS' , pero unir la segunda cláusula 'compare' => 'NOT EXISTS' con OR (como debemos) desordena el orden. El resultado es que LEFT JOIN se usa para unir tanto la primera cláusula / tabla como la segunda cláusula / tabla, y la forma en que WP pone todo junto significa que la tabla creada con 'compare' => 'EXISTS' se está completando con meta_values desde CUALQUIER campo personalizado, no solo el campo 'custom_author_name' en el que estamos interesados. Por lo tanto, creo que ordenar por esa cláusula / tabla solo dará los resultados deseados si el tipo de publicación en particular de 'noticias' solo tiene un único campo personalizado.

La solución que funcionó para mi situación fue ordenar por la otra cláusula / tabla: la NO EXISTA. Al parecer, es contraintuitivo, pero debido a la forma en que WP analiza las variables de consulta, es en esta tabla donde meta_value solo se llena con el campo personalizado que buscamos.

(La única forma en que me di cuenta de esto fue ejecutando el equivalente de esta consulta para mi 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

Todo lo que he hecho es cambiar las columnas que se muestran y eliminar la cláusula GROUP BY. Esto luego me mostró lo que estaba pasando: que la columna postmeta.meta_value estaba obteniendo valores de todas las meta_keys, mientras que la columna mt1.meta_value estaba obteniendo solo meta_values del campo personalizado de noticias.)

La solución

Como dice @Manny Fleurmond, es la primera cláusula que se utiliza para el pedido, por lo que la respuesta es simplemente intercambiar las cláusulas, dando esto:

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

Alternativamente, puede hacer las matrices asociativas de orden y las cláusulas por la clave correspondiente, de esta manera:

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

Lea otras preguntas en las etiquetas