query_posts -> usando meta_compare / donde meta value es menor, mayor o igual
Estoy usando query_posts( $args )
para filtrar el Loop.
Quiero filtrar posts basados en su meta_value
"vote", a veces menor que, a veces igual y así sucesivamente....
Definitivamente quiero usar la función query_posts()
y pasar mi filtro a través de $args
!
No quiero usar add_filter('posts_where', 'filter_where');
y luego agregar una declaración AND
a la consulta.
Quiero usar la funcionalidad proporcionada por WordPress para filtrar posts con meta_key
, meta_value
y meta_compare
así:
$args = array( 'meta_key'=>'vote', 'meta_compare'=>'>=', 'meta_value'=>5, 'posts_per_page'=>100 ) )
query_posts( $args );
El resultado de esto es:
SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND wp_postmeta.meta_key = 'vote' AND wp_postmeta.meta_value >= '5' GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 100
El problema de esto es:
wp_postmeta.meta_value >= '5'
Debería ser:
wp_postmeta.meta_value >= 5
Entonces funcionaría bien.
No entiendo por qué WordPress agrega comillas.
Estoy usando el parámetro predefinido de WordPress (<
, >
, <=
, >=
) y es obvio que esto solo funcionará con números y no con cadenas que necesitarían estar entre comillas.
La documentación dice:
Devuelve posts con una clave de campo personalizado 'miles' con un valor de campo personalizado que es MENOR O IGUAL A 22
query_posts('meta_key=miles&meta_compare=<=&meta_value=22');

Desde WP 3.1, puedes convertir el valor meta a cualquier tipo que desees usando el argumento 'type' en 'meta_query':
$args = array(
'meta_query'=> array(
array(
'key' => 'vote',
'compare' => '>=',
'value' => 5,
'type' => 'numeric',
)
)
'posts_per_page' => 100
) );
query_posts( $args );

Al echar un vistazo rápido a la documentación, parece que meta_value
está destinado para cadenas de texto y para valores numéricos existe meta_value_num
.
Consulta Parámetros de Orderby
Actualización
Investigué un poco.
meta_value_num
efectivamente es ignorado para el propósito de filtrado. Creo que simplemente olvidaron agregar esa parte. :)
El problema es que WP_Query
recibe correctamente el número como int
(pasarlo como array no importa), pero pasa la condición generada de meta_compare
a través de $wpdb->prepare()
y marca explícitamente el valor como %s
(cadena). En este caso, prepare
fuerza el entrecomillado simple.
Así que parece que tendrás que filtrar posts_where
después de todo. Puedes intentar simplemente eliminar las comillas de esa cadena específica en lugar de generar la condición manualmente.

Recomiendo analizar tu array $args
y convertirlo a una cadena antes de pasarlo a query_posts
. Cuando creas el array $args
, el sistema convertirá automáticamente el número 5 en una cadena "5" cuando el array se convierta de nuevo en una cadena.
Así que usa esto en su lugar:
query_posts('meta_key=vote&meta_compare=>=&meta_value=5&posts_per_page=100');
Eso sigue pasando la misma información a query_posts, pero debería pasar el número 5 en lugar de la cadena "5".
Actualización
Como ahora hemos descubierto que meta_value almacena cadenas en lugar de números, y no se puede hacer efectivamente una comparación de mayor que/menor que con cadenas. Sin embargo, después de investigar un poco más, encontré la bandera de consulta meta_value_num
.
Si ejecutas la siguiente llamada a query_posts
:
query_posts('meta_key=vote&meta_compare=>=&meta_value=5&posts_per_page=100&orderby=meta_value_num');
Entonces deberías obtener el comportamiento que deseas. meta_value_num
le indica a WordPress que evalúe tus meta_value
como números en lugar de cadenas.

Intenté que produjera exactamente lo mismo. De hecho, seguí el camino de función en función en el núcleo de WordPress y la cadena se divide en & y se convierte en un array antes de procesarse...

Según el Codex: Nota: el valor 99 será considerado mayor que 100 ya que los datos se almacenan como cadenas, no como números.
Por lo tanto, meta_value
se está almacenando como una cadena... por eso WordPress añade las comillas. Mi siguiente pregunta sería, ¿puedes describir cómo esto "no funciona"? ¿Está devolviendo datos al menos?

¡Sí, tienes razón! Como dije antes, no importa lo que haga, se envuelve en '5'. MySQL no puede comparar algo a nivel numérico si está envuelto en '', así que me pregunto por qué WordPress ha implementado esta función si no funciona. No, devuelve resultados pero simplemente no los deseados.

Mira mi actualización anterior. Agregar orderby=meta_value_num
debería solucionar el problema si estás ejecutando WP 2.8.4 o posterior.

Gracias por todos los esfuerzos. Pero eso tampoco funciona. Realmente investigué a fondo este tema y simplemente no está soportado por WP. ¡Crearé un ticket para este issue porque es realmente fácil de solucionar y permitiría muchas cosas!

@Joakim Sí, esto está soportado por WP. Lee el ticket que referencié arriba donde se agregó esta funcionalidad. Tal vez podrías compartirnos un fragmento de código más grande de lo que estás intentando hacer... podría haber un problema diferente.

WordPress trata todos los 'valores' como cadenas de texto y les añade comillas simples en la consulta final, por lo que SQL también se ve obligado a tratarlos como cadenas y no como números. Puedes eliminar estas comillas con un filtro:
add_filter('get_meta_sql', function($data) {
$regex = "/'(-?\d+)'/";
$data['where'] = preg_replace($regex, "$1", $data['where']);
return $data;
});
Y asegúrate de tener 'type' => 'NUMERIC'
o similar
