Orderby meta_value returnează doar postările care au meta_key existent

14 mai 2015, 02:14:21
Vizualizări: 13.6K
Voturi: 11

Am următorul 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 rezultate deoarece există doar 10 postări news cu un meta_key = custom_author_name. Dar există sute de postări news care nu au un rând post_meta cu acel meta_key specific. Rețineți că nu există nicio meta_query implicată. Nu este atribuită nicio meta_value, pentru că încerc doar să ordonez postările după meta_key, nu să le filtrez după meta_value.

Nu ar trebui ca orderby să selecteze toate postările? și doar să le ordoneze?

Dacă da, de ce rezultatul este filtrat? Dacă meta_key nu este găsit, de ce să nu folosească un șir gol sau o potrivire generală?

Dacă nu, de ce nu?

Dacă introduc un meta_key pentru fiecare postare news (chiar dacă este un șir gol), atunci obțin rezultatul așteptat. Dar asta pare a fi o mulțime de rânduri inutile în tabel.

0
Toate răspunsurile la întrebare 4
1
12

După cum a menționat @ambroseya în răspunsul său, funcționarea este conformă așteptărilor. Odată ce declarați o interogare meta, chiar dacă nu căutați o valoare specifică, aceasta va interoga doar postările care au acea cheie meta declarată. Dacă doriți să includeți toate postările și să le sortați după cheia meta, utilizați următorul cod:

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

Ceea ce face acest cod este să utilizeze o interogare meta avansată care caută postări care au și nu au acea cheie meta declarată. Deoarece cea cu EXISTS este prima, atunci când sortați după meta_value, aceasta va folosi prima interogare.

14 mai 2015 05:41:31
Comentarii

Acest lucru nu a schimbat deloc ordinea pentru mine, când am folosit 'orderby' => 'meta_value', ordinea s-a schimbat, dar nu avea nicio legătură cu câmpul meta real.

Jake Jake
26 oct. 2016 14:21:38
1

Am încercat să aplic răspunsul lui @Manny Fleurmond și, la fel ca @Jake, nu am reușit să-l fac să funcționeze, chiar și după ce am corectat greșeala că 'orderby' => 'meta_key' ar trebui să fie 'orderby' => 'meta_value'. (Și pentru completitudine, ar trebui să fie 'posts_per_page', nu 'post_per_page', dar asta nu afectează problema analizată.)

Dacă te uiți la interogarea SQL generată efectiv de răspunsul lui @Manny Fleurmond (după corectarea greșelilor), aceasta este rezultatul:

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

Aceasta ilustrează modul în care WP parsează variabilele de interogare: creează un tabel pentru fiecare clauză meta_query, apoi determină cum să le unească și după ce să ordoneze. Ordonarea ar funcționa bine dacă ai folosi doar o singură clauză cu 'compare' => 'EXISTS', dar unirea celei de-a doua clauze 'compare' => 'NOT EXISTS' cu OR (așa cum trebuie) încurcă ordonarea. Rezultatul este că LEFT JOIN este folosit pentru a uni atât prima clauză/tabel, cât și a doua clauză/tabel - iar modul în care WP le asamblează înseamnă că tabelul creat folosind 'compare' => 'EXISTS' este, de fapt, populat cu meta_values din ORICE câmp personalizat, nu doar din câmpul 'custom_author_name' la care suntem interesați. Deci, cred că ordonarea după acea clauză/tabel va oferi rezultatele dorite doar dacă tipul de postare 'news' are un singur câmp personalizat.

Soluția care a funcționat în cazul meu a fost ordonarea după cealaltă clauză/tabel - cea NOT EXISTS. Pare contraintuitiv, știu, dar din cauza modului în care WP parsează variabilele de interogare, acest tabel este cel în care meta_value este populat doar de câmpul personalizat pe care îl căutăm.

(Singurul mod prin care am descoperit acest lucru a fost prin rularea unei interogări echivalente pentru cazul meu:

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

Tot ce am făcut a fost să schimb coloanele afișate și să elimin clauza GROUP BY. Acest lucru mi-a arătat ce se întâmpla - că coloana postmeta.meta_value extragea valori din toate meta_key-urile, în timp ce coloana mt1.meta_value extragea doar meta_values din câmpul personalizat news.)

Soluția

Așa cum spune @Manny Fleurmond, prima clauză este cea folosită pentru orderby, deci răspunsul este doar să inversăm clauzele, obținând astfel:

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

Alternativ, poți face clauzele array-uri asociative și să ordonezi după cheia corespunzătoare, astfel:

$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);
5 nov. 2017 11:09:23
Comentarii

Este important de menționat că, dacă cheia meta custom_author_name este setată și apoi eliminată, acel meta_key va răspunde la EXISTS și efectul va fi că acestea vor fi afișate împreună cu postările care au un custom_author_name. În cazul meu, am o casetă de bifare, așa că folosesc "value" => "1" în loc de EXISTS, dar pentru șirurile de caractere va fi necesară o abordare diferită.

djb djb
23 mai 2019 18:20:17
0

Așa funcționează de fapt.

Dacă doriți să faceți acest lucru fără a adăuga rânduri în tabel, va trebui să faceți două interogări. Una cu meta_key care are rezultatele limitate, iar cealaltă care obține întreaga listă; apoi utilizați PHP pentru a compara cele două rezultate ale interogărilor (eventual eliminând rezultatele meta_key din cealaltă interogare pentru a elimina duplicatele sau orice altceva are sens în setarea dvs.).

14 mai 2015 04:45:13
0

Din păcate, funcționarea WP_Query nu este așa. De îndată ce adaugi componenta "meta", ai creat un fel de filtru. Afișează $query->request și vei înțelege la ce mă refer.

În al doilea rând, WP_Query nu acceptă ordonarea după un cheie meta. Poți ordona după o valoare meta pentru o anumită cheie, dar nu după cheia în sine. Din nou, afișează interogarea pentru a vedea la ce mă refer. Vei observa că componentele de "order" dispar dacă încerci.

Cea mai elegantă soluție pentru a face acest lucru să funcționeze, în opinia mea, este folosirea a câtorva filtre scurte:

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
14 mai 2015 16:41:30