Meta consulta compleja con 3 teclas

8

Creo que el problema se relaciona esencialmente con la estructura de consulta SQL y no soy un experto ...

Necesito buscar publicaciones (tipo de publicación personalizada) por 2 parámetros:

  1. pd_city

  2. pd_country

Tenga en cuenta que la relación meta_query es 'OR', por lo que si cualquiera de los dos anteriores es LIKE, deberíamos tener algunos resultados.

¡La tercera clave (es_patrocinada) se usa para ordenar las publicaciones! Puede ser 1 o 0 y las publicaciones cuyo valor "es_patrocinado" es igual a 1 deben aparecer en la parte superior.

Así que aquí está lo de WordPress:

    $sfp_query_args = array(
        'sfp_complex_search' => 'yeap', 
        'tax_query' => array( array( 'taxonomy' => 'sfp_post_category', 'terms' => $term_id ) ),
        //'meta_key' => 'is_sponsored',
        'post_type' => 'sfpposts',
        'post_status' => 'publish',
        'showposts' => (int)$per_page,
        'paged' => $paged, 
        'meta_query' => array( 'relation' => 'OR', 
                array( 'key' => 'pd_city', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), 
                array( 'key' => 'pd_country', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), 
                array( 'key' => 'is_sponsored' )
                )
    );
$sfp_search = new WP_Query( $sfp_query_args );

También necesito filtrar los resultados con "posts_orderby" para que los patrocinados lleguen a la cima:

add_filter( 'posts_orderby', 'sfp_modify_search' );
function sfp_modify_search( $orderby ) {
    if( !is_admin() && is_page( $this->options[ 'sfp_page_entries_search' ] ) ) {
        global $wpdb;
        $orderby = " CASE WHEN mt2.meta_value = 0 THEN 1 END, $wpdb->posts.post_date DESC ";
    }
    return $orderby;
}

El problema real se basa en que con esta consulta se devuelven TODAS LAS PUBLICACIONES de "sfp_post_category", no solo las que coinciden con "pd_city" o "pd_country" porque TODAS LAS PUBLICACIONES TIENEN metaclave "patrocinada" (y el valor establecido en 1 o 0). Una vez más: ¡se necesita "es_patrocinado" para ordenar!

Cuando var_dump

var_dump( $sfp_search->request );

... WordPress 'sql se ve así:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
WHERE 1=1 
AND ( wp_term_relationships.term_taxonomy_id IN (77) ) 
AND wp_posts.post_type = 'sfpposts' 
AND (wp_posts.post_status = 'publish') 
AND ( (wp_postmeta.meta_key = 'pd_city' 
AND CAST(wp_postmeta.meta_value AS CHAR) 
LIKE '%something%') 
OR (mt1.meta_key = 'pd_country' 
AND CAST(mt1.meta_value AS CHAR) 
LIKE '%something%') 
OR mt2.meta_key = 'is_sponsored' ) 
GROUP BY wp_posts.ID 
ORDER BY CASE WHEN mt2.meta_value = 0 THEN 1 END, wp_posts.post_date DESC 
LIMIT 0, 10

¿Cómo elimino todas las publicaciones que no coinciden con "pd_city" o "pd_country" de los resultados?

Dameer
fuente

Respuestas:

6

El culpable

El culpable del asunto son las metaconsultas que no admiten relaciones diferentes y / o anidadas, una deficiencia por cierto, que también me ha vuelto loco antes. En una instancia reciente con un escenario de búsqueda también.

Lo que quiere hacer simplemente no se puede lograr con WP_Querytan solo un ciclo.
Como parece haber notado, si coloca la clave de clasificación en la meta_querymatriz o fuera de ella como un argumento de consulta general, no hay diferencia. Si establece la relación de ORmetaconsulta y especifica un meta_keyargumento de consulta en cualquier lugar sin establecer el meta_valueparámetro que lo acompaña , la consulta siempre devolverá al menos todas las publicaciones donde se establece esa meta_key.
Por cierto y en aras de la exhaustividad: cuando utiliza un solo meta_query con !=un valor para meta_compare, la consulta devolverá todos los resultados con el meta_keyconjunto y no meta_valueserá igual al dado ; nodevolver cualquier publicación que no tenga la meta_keyutilizada en absoluto. Otro punto donde las metaconsultas fallan.

Solución 1

Veo dos opciones. Por un lado, podría omitir la is_sponsoredmetaclave de la consulta, omitir también la paginación, obtener las publicaciones correctas y ordenarlas con una segunda instancia de WP_Query, pasando los ID de publicaciones filtradas a través del post__inparámetro:

$sfp_search_args = array(
    'sfp_complex_search' => 'yeap', 
    'tax_query' => array( array( 'taxonomy' => 'sfp_post_category', 'terms' => $term_id ) ),
    'post_type' => 'sfpposts',
    'post_status' => 'publish',
    'meta_query' => array(
        'relation' => 'OR', 
        array( 'key' => 'pd_city', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), 
        array( 'key' => 'pd_country', 'value' => $sfp_search_meta, 'compare' => 'LIKE' )
    )
);

$sfp_search = new WP_Query( $sfp_search_args );
$post_ids = array();
while ( $sfp_search->have_posts() ) : $sfp_search->next_post();
    $post_ids[] = $sfp_search->post->ID;
endwhile;

$sfp_ordered_args(
    'post__in' => $post_ids,
    // note that 'showposts' is deprected
    'posts_per_page' => (int)$per_page, 
    'paged' => $paged,
    'meta_key' => 'is_sponsored',
    'order' => 'DESC',
    'orderby' => 'meta_value_num date'
);
$sfp_ordered = new WP_Query( $sfp_ordered_args );
while ( $sfp_ordered->have_posts() ) : $sfp_ordered->next_post();
    // display posts
endwhile;

Tenga en cuenta que el $orderbyparámetro de WP_Querytomará múltiples valores separados por un espacio. Su modificación de búsqueda puede ser más compleja de lo necesario.

Solución 2

Como me gusta su idea de var_dumping de la requestpropiedad del objeto de consulta , permítame activar una sugerencia secundaria rápida, y, nota, no probada:

Si ha modificado ligeramente el SQL dada cambiando el operador lógico de OR mt2.meta_key = 'is_sponsored'a ANDy moviéndolo en consecuencia, se puede tirar de los postes con $wpdb:

$sfp_post_ids = $wpdb->get_col(
    "
    SELECT wp_posts.ID 
    FROM wp_posts 
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
    INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
    INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
    INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
    WHERE 1=1 
    AND ( wp_term_relationships.term_taxonomy_id = $term_id ) 
    AND wp_posts.post_type = 'sfpposts' 
    AND (wp_posts.post_status = 'publish') 
    AND ( (wp_postmeta.meta_key = 'pd_city' 
    AND CAST(wp_postmeta.meta_value AS CHAR) 
    LIKE '%$sfp_search_meta%') 
    OR (mt1.meta_key = 'pd_country' 
    AND CAST(mt1.meta_value AS CHAR) 
    LIKE '%$sfp_search_meta%') )
    AND mt2.meta_key = 'is_sponsored' 
    GROUP BY wp_posts.ID 
    ORDER BY CASE WHEN mt2.meta_value = 0 THEN 1 END, wp_posts.post_date DESC
    "
);

En este punto, también tiene dos opciones:
iterar sobre la $sfp_post_idsmatriz con un simple foreachy extraer los datos de la publicación get_post()individualmente dentro de ese bucle o, si desea las sutilezas de WP_Query: paginación, etiquetas de plantilla, etc., alimentar $sfp_post_idsel post__inparámetro como en la Solución 1.

Johannes Pille
fuente
2

Todo esto sucede debido a la ORrelación meta_queryy la forma en que WordPress genera la cadena de consulta real. Terminé enganchándome en el posts_clausesfiltro para modificar las partes wherey orderbyde la consulta:

public function wpse_68002_orderby_fix($pieces){
    global $wpdb;
    $pieces['where']  .= " AND $wpdb->postmeta.meta_key = 'your_meta_key'"; // <--- update here with your meta_key name
    $pieces['orderby']  = "$wpdb->postmeta.meta_value ASC";
    return $pieces;
}

Simplemente agregue el filtro antes de configurar su objeto WP_Query y luego asegúrese de eliminarlo después de ejecutar su consulta para no afectar otras consultas:

    add_filter( 'posts_clauses', 'wpse_68002_orderby_fix', 20, 1 );
    $query = new WP_Query($args);
    $result = $query->get_posts();
    remove_filter( 'posts_clauses', 'wpse_68002_orderby_fix', 20 );

Recuerde que debe dejar de lado el meta_keyy orderbyde los argumentos de la consulta.

Parham
fuente
1

Eso es complicado :)

Iba a sugerir que es posible que no necesite tener el array( 'key' => 'is_sponsored' )valor en la matriz 'meta_query' y que pueda hacerlo agregando una 'meta_key' a la matriz principal, pero parece que lo ha intentado. ¿Obtuviste los mismos resultados?

JOINs puede complicar las cosas. Te enganchaste posts_orderby. ¿Has considerado conectarte posts_fieldsy agregar una subconsulta que te daría tu meta_valor?

(SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = 'is_sponsored' AND post_id = {$wpdb->posts}.ID) as is_sponsored

s_ha_dum
fuente