Meta query complessa con 3 chiavi


8

Penso che il problema riguardi essenzialmente la struttura delle query sql e non sono un esperto ....

Devo cercare post (tipo di post personalizzato) in base a 2 parametri:

  1. pd_city

  2. pd_country

Si noti che la relazione meta_query è 'OR', quindi se una delle due sopra è COME, dovremmo avere dei risultati.

La terza chiave (is_sponsored) viene utilizzata per ordinare i post! Può essere 1 o 0 e i post il cui valore "is_sponsored" è uguale a 1 dovrebbero essere elencati in alto.

Quindi ecco la cosa di 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 );

Devo anche filtrare i risultati con "posts_orderby" per ottenere quelli sponsorizzati in alto:

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;
}

Il vero problema si basa infatti sul fatto che con questa query TUTTI I POSTI da "sfp_post_category" vengono restituiti, non solo quelli che corrispondono a "pd_city" o "pd_country" perché TUTTI I POSTI HANNO "is_sponsored" chiave meta (e valore impostato su 1 o 0). Ancora una volta: "is_sponsored" è necessario per l'ordinamento!

Quando var_dump

var_dump( $sfp_search->request );

... sql di WordPress si presenta così:

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

Come posso eliminare tutti i post che non corrispondono a "pd_city" o "pd_country" dai risultati?

Risposte:


6

Il colpevole

Il colpevole della questione sono le meta-query che non supportano relazioni diverse e / o nidificate - un difetto tra l'altro, che mi ha fatto impazzire anche prima. In una recente istanza anche con uno scenario di ricerca.

Quello che vuoi fare semplicemente non può essere realizzato con WP_Queryun solo ciclo.
Come sembra aver notato, se si inserisce la chiave di ordinamento meta_querynell'array o all'esterno di essa come argomento di query generale non fa differenza. Se imposti la relazione della meta query ORe specifichi un meta_keypunto qualsiasi degli argomenti della query senza impostare il meta_valueparametro di accompagnamento , la query restituirà sempre almeno tutti i post in cui è impostata quella meta_key.
Tra l'altro e per ragioni di completezza: Quando si utilizza un singolo meta_query con !=come valore per meta_comparela query restituirà tutti i risultati con il meta_keyset e non è uguale al dato meta_value- lo farà , nonrestituire tutti i post che non hanno meta_keyutilizzato affatto. Un altro punto in cui le meta query falliscono.

Soluzione 1

Vedo due opzioni. Per uno, potresti omettere la is_sponsoredmeta chiave dalla query, omettere anche l'impaginazione, ottenere i post corretti ed eseguire l'ordinamento con una seconda istanza di WP_Query, passandogli gli ID post filtrati tramite il post__inparametro:

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

Si noti che il $orderbyparametro di WP_Queryprenderà più valori separati da uno spazio. La modifica della ricerca potrebbe essere più complessa del necessario.

Soluzione 2

Dal momento che mi piace la tua idea di var_dumping della requestproprietà dell'oggetto query , fammi dare un suggerimento secondario - e, non testato - secondario:

Se hai modificato leggermente il dato SQL cambiando l'operatore logico di OR mt2.meta_key = 'is_sponsored'in ANDe spostandolo di conseguenza, potresti estrarre i post 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
    "
);

A questo punto hai anche due opzioni:
o iterare $sfp_post_idssull'array con un semplice foreached estrarre i dati dei post get_post()individualmente all'interno di quel ciclo, o, se vuoi le prelibatezze di WP_Query- paginazione, tag modello e così via - feed $sfp_post_idsal post__inparametro come nella soluzione 1.


2

Tutto ciò accade a causa della ORrelazione attiva meta_querye del modo in cui WordPress genera la stringa di query effettiva. Ho finito per agganciare nel posts_clausesfiltro per modificare le wheree orderbypezzi della query:

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;
}

Basta aggiungere il filtro prima di configurare l'oggetto WP_Query e quindi assicurarsi di rimuoverlo dopo aver eseguito la query per non influire su altre query:

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

Ricorda di tralasciare la meta_keye orderbydalla query args.


1

È complicato :)

Stavo per suggerire che potresti non aver bisogno di avere il array( 'key' => 'is_sponsored' )valore nell'array 'meta_query' e che potresti farlo aggiungendo un 'meta_key' all'array principale, ma sembra che tu ci abbia provato. Hai ottenuto gli stessi risultati?

JOINs può complicare le cose. Ti sei agganciato posts_orderby. Hai preso in considerazione l'aggancio posts_fieldse l'aggiunta di una sottoquery che ti farebbe ottenere il tuo meta_valore?

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.