Orderby meta_value solo devuelve publicaciones que tienen meta_key existente

10

Tengo el 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 10 newspublicaciones con a meta_key = custom_author_name. Pero hay cientos de newspublicaciones que no tienen una fila post_meta con esa meta_key específica. Tenga en cuenta que no hay meta_query involucrado. No se asigna meta_value, porque solo intento ordenar las publicaciones por meta_key, y no filtrar por meta_value.

¿No debería ordenar por seleccionar 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 una coincidencia?

¿Si no, porque 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í.

gdaniel
fuente

Respuestas:

10

Como se indica en la respuesta de @ ambroseya, se supone que funciona así. Una vez que declara una metaconsulta, incluso si no está buscando un valor específico, solo consultará las publicaciones con esa metaclave 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_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;

Lo que esto hace es usar una meta consulta avanzada que busca publicaciones que tienen y no tienen esa metaclave declarada. Dado que el que tiene EXISTSes el primero, cuando ordena por meta_value, usará la primera consulta.

Manny Fleurmond
fuente
3
Esto no cambió el orden en absoluto para mí, cuando lo usé 'orderby' => 'meta_value', cambió el orden, pero no tenía nada que ver con el metacampo real.
Jake
3

Intenté aplicar la respuesta de @Manny Fleurmond y, al igual que @Jake, no pude hacerlo funcionar incluso después de corregir el error tipográfico que 'orderby' => 'meta_key'debería ser 'orderby' => 'meta_value'. (Y para completar, hay que 'posts_per_page'no 'post_per_page', pero que no afecta a la cuestión está examinando.)

Si observa la consulta SQL realmente generada por la respuesta de @Manny Fleurmond (después de corregir los errores tipográficos), esto es lo que obtiene:

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 analiza los vars de consulta: está creando una tabla para cada cláusula meta_query, luego descubre cómo unirlos y por qué ordenarlos. El orden funcionaría bien si solo usara una sola cláusula con 'compare' => 'EXISTS', pero unir la segunda 'compare' => 'NOT EXISTS'cláusula con OR (como debemos) arruina 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 reúne todo significa que la tabla creada usando en 'compare' => 'EXISTS'realidad se llena con meta_valores de CUALQUIER campo personalizado, no solo el 'custom_author_name'campo en el que estamos interesados. Así que creo que ordenar por esa cláusula / tabla solo dará los resultados deseados si el post_type particular de 'noticias' solo tiene un campo personalizado único.

La solución que funcionó para mi situación fue ordenar por la otra cláusula / tabla, la NO EXISTENTE. Parece contradictorio, lo sé, pero debido a la forma en que WP analiza los vars de consulta, es en esta tabla donde meta_valuese completa solo el campo personalizado que buscamos.

(La única forma en que descubrí 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 sucediendo: 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_valores del campo personalizado de noticias).

La solución

Como dice @Manny Fleurmond, es la primera cláusula que se usa para el pedido, por lo que la respuesta es simplemente cambiar 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 que las cláusulas sean matrices asociativas y se ordenen 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);
jlad26
fuente
Vale la pena señalar que si la meta clave custom_author_nameestá configurada y luego desactivada, eso meta_keyresponderá EXISTSy el efecto será que esas burbujas aparecerán junto a las publicaciones que sí tienen custom_author_name. En mi caso, tengo una casilla de verificación, así que estoy usando en "value" => "1"lugar de EXISTS, pero las cadenas necesitarán un enfoque diferente.
djb
1

Así es como funciona.

Si desea hacerlo sin agregar filas de 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 eliminando los resultados de meta_key de la otra consulta para eliminar duplicados, o lo que tenga sentido en su configuración).

ambroseya
fuente
0

Desafortunadamente, no es así como WP_Queryfunciona. Tan pronto como agregue ese componente "meta", habrá creado una especie de filtro. Volcado $query->requesty verás lo que quiero decir.

En segundo lugar, WP_Queryno admite el pedido por una meta clave en absoluto. Se puede pedir por un meta valor para una clave particular, pero no por la propia llave. Nuevamente, volcar la consulta para ver a qué me refiero. Notarás que los componentes de "pedido" desaparecen 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
s_ha_dum
fuente