Ordina per meta_value restituisce solo i post che hanno meta_key esistente


10

Ho il seguente 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 risultati perché ci sono solo 10 newspost con a meta_key = custom_author_name. Ma ci sono centinaia di newspost che non hanno una riga post_meta con quella specifica meta_key. Si noti che non ci sono meta_query coinvolte. Nessun meta_value è assegnato, perché sto solo cercando di ordinare i post per meta_key e non filtrare per meta_value.

Non dovresti ordinare selezionando tutti i post? e semplicemente ordinarli?

In tal caso, perché il risultato viene filtrato? Se la meta_key non viene trovata, perché non usare solo una stringa vuota o una corrispondenza tutto?

In caso contrario, perché no?

Se inserisco una meta_key per ogni post di notizie (anche se è una stringa vuota), ottengo il risultato atteso. Ma sembra un sacco di righe di tabella che non hanno bisogno di essere lì.

Risposte:


10

Come affermato nella risposta di @ ambroseya, dovrebbe funzionare così. Una volta dichiarata una meta query, anche se non stai cercando un valore specifico, interrogherà solo i post con quella meta chiave dichiarata. Se vuoi includere tutti i post, ordinali in base alla meta chiave, usa il seguente codice:

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

Quello che fa è usare una meta query avanzata che cerca post che fanno e non hanno quella meta chiave dichiarata. Poiché quello con EXISTSè il primo, quando si ordina per meta_value, utilizzerà la prima query.


3
Questo non ha cambiato affatto l'ordine per me, quando l'ho usato 'orderby' => 'meta_value', ha cambiato l'ordine, ma non aveva nulla a che fare con il meta field reale.
Jake,

3

Ho provato ad applicare la risposta di @Manny Fleurmond e come @Jake non sono riuscito a farlo funzionare anche dopo aver corretto l'errore di battitura che 'orderby' => 'meta_key'dovrebbe essere 'orderby' => 'meta_value'. (E per completezza che dovrebbe essere 'posts_per_page'non 'post_per_page', ma che non pregiudica la questione di essere guardato.)

Se guardi alla query SQL effettivamente generata dalla risposta di @Manny Fleurmond (dopo aver corretto i refusi) questo è ciò che ottieni:

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

Questo illustra il modo in cui WP sta analizzando le query vars: sta creando una tabella per ogni clausola meta_query, quindi capendo come unirle e cosa ordinare. L'ordinamento funzionerebbe correttamente se si utilizzava una sola clausola con 'compare' => 'EXISTS', ma unendo la seconda 'compare' => 'NOT EXISTS'clausola con OR (come dobbiamo) si incasina l'ordinamento. Il risultato è che LEFT JOIN è usato per unire sia la prima clausola / tabella che la seconda clausola / tabella - e il modo in cui WP mette insieme tutto ciò significa che la tabella creata usando 'compare' => 'EXISTS'viene effettivamente popolata con meta_valori da QUALSIASI campo personalizzato, non solo il 'custom_author_name'campo a cui siamo interessati. Quindi penso che ordinare per quella clausola / tabella fornirà i risultati desiderati solo se il particolare post_type di 'news' ha un solo campo personalizzato.

La soluzione che ha funzionato per la mia situazione è stata quella di ordinare dall'altra clausola / tabella - quella NON ESISTE. Apparentemente controintuitivo, lo so, ma a causa del modo in cui WP analizza le query, questa tabella meta_valueè popolata solo dal campo personalizzato che stiamo cercando.

(L'unico modo in cui l'ho capito era eseguendo l'equivalente di questa query per il mio 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

Tutto quello che ho fatto è stato modificare le colonne mostrate e rimuovere la clausola GROUP BY. Questo poi mi ha mostrato cosa stava succedendo - che la colonna postmeta.meta_value stava estraendo i valori da tutti i meta_key, mentre la colonna mt1.meta_value stava estraendo solo i meta_valori dal campo personalizzato delle notizie.)

La soluzione

Come dice @Manny Fleurmond, è la prima clausola utilizzata per l'ordine, quindi la risposta è solo scambiare le clausole, dando questo:

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

In alternativa puoi creare le clausole array associativi e ordinare in base alla chiave corrispondente, in questo modo:

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

Vale la pena sottolineare che se la meta-chiave custom_author_nameè impostata e quindi non impostata, ciò meta_keyrisponderà EXISTSe l'effetto sarà che quelli saranno gorgogliati insieme ai post che hanno un custom_author_name. Nel mio caso ho una casella di controllo, quindi sto usando "value" => "1"invece di EXISTS, ma le stringhe avranno bisogno di un approccio diverso.
DJ

1

In realtà è così che funziona.

Se vuoi farlo senza aggiungere righe di tabella, dovrai fare due query. Uno con la meta_key che ha risultati limitati e l'altro che ottiene l'intero elenco; quindi utilizza PHP per confrontare i due risultati della query (eventualmente rimuovendo i risultati della meta_key dall'altra query per rimuovere i duplicati o qualsiasi cosa abbia senso nelle tue impostazioni).


0

Sfortunatamente, non è così che WP_Queryfunziona. Non appena aggiungi quel componente "meta", hai creato una specie di filtro. Dump $query->requeste vedrai cosa intendo.

In secondo luogo, WP_Querynon supporta affatto l'ordinamento tramite una meta chiave . È possibile ordinare in base a un valore meta per una chiave particolare ma no in base alla chiave stessa. Ancora una volta, scarica la query per vedere cosa intendo. Noterai che i componenti "ordine" si ritirano se provi.

Il modo più pulito per farlo funzionare, a mio avviso, è un paio di filtri brevi:

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
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.