Kueri meta kompleks dengan 3 kunci

8

Saya pikir masalahnya pada dasarnya berkaitan dengan struktur kueri sql dan saya bukan ahli ....

Saya perlu mencari posting (tipe posting khusus) dengan 2 parameter:

  1. pd_city

  2. pd_country

Harap perhatikan bahwa relasi meta_query adalah 'ATAU' jadi jika salah satu dari dua di atas SEPERTI kami harus memiliki beberapa hasil.

Kunci ketiga (is_sponsored) digunakan untuk mengurutkan posting! Itu bisa 1 atau 0 dan posting yang "is_sponsored" nilainya sama dengan 1 harus terdaftar di atas.

Jadi, inilah hal 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 );

Saya juga perlu memfilter hasil dengan "posts_orderby" untuk mendapatkan yang disponsori ke atas:

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

Masalah sebenarnya bergantung pada kenyataan bahwa dengan kueri ini SEMUA POS dari "sfp_post_category" dikembalikan, tidak hanya mereka yang cocok dengan "pd_city" atau "pd_country" karena SEMUA POSTIMEWA "is_sponsored" kunci meta (dan nilai diatur ke 1 atau 0). Sekali lagi: "is_sponsored" diperlukan untuk menyortir!

Saat var_dump

var_dump( $sfp_search->request );

... sql WordPress terlihat seperti ini:

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

Bagaimana cara menghilangkan semua posting yang tidak cocok dengan "pd_city" atau "pd_country" dari hasil?

Dameer
sumber

Jawaban:

6

Pelakunya

Penyebab masalah ini adalah meta queries yang tidak mendukung hubungan yang berbeda dan / atau bersarang - kekurangan yang telah membuat saya gila sebelumnya juga. Dalam contoh terbaru dengan skenario pencarian juga.

Apa yang ingin Anda lakukan tidak bisa dicapai dengan WP_Querysatu loop saja.
Seperti yang Anda perhatikan, apakah Anda meletakkan kunci sortir di dalam meta_queryarray atau di luarnya sebagai argumen permintaan umum tidak membuat perbedaan. Jika Anda mengatur relasi meta query ke ORdan menentukan di meta_keymana saja dari argumen kueri tanpa menetapkan meta_valueparameter yang menyertainya , kueri akan selalu mengembalikan setidaknya semua posting di mana meta_key itu ditetapkan.
Ngomong-ngomong dan demi kelengkapan: Ketika Anda menggunakan satu meta_query dengan !=sebagai nilai untuk meta_compare, kueri akan mengembalikan semua hasil dengan meta_keyset dan tidak sama dengan yang diberikan meta_value- itu tidak akankembalikan tulisan apa pun yang tidak meta_keydigunakan sama sekali. Titik lain di mana meta kueri gagal.

Solusi 1

Saya melihat dua opsi. Untuk satu, Anda bisa menghilangkan is_sponsoredkunci meta dari kueri, menghilangkan pagination juga, mendapatkan posting yang benar dan melakukan penyortiran dengan instance kedua WP_Query, meneruskannya ID posting yang difilter melalui post__inparameter:

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

Perhatikan, bahwa $orderbyparamter of WP_Queryakan mengambil beberapa nilai yang dipisahkan oleh spasi. Modifikasi pencarian Anda mungkin lebih kompleks daripada yang diperlukan.

Solusi 2

Karena saya menyukai gagasan Anda tentang var_dumping requestproperti objek kueri , izinkan saya menjalankan saran sekunder - dan, perhatikan, belum teruji -:

Jika Anda sedikit memodifikasi SQL yang diberikan dengan mengubah operator logis OR mt2.meta_key = 'is_sponsored'menjadi ANDdan memindahkannya, Anda dapat menarik posting dengan $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
    "
);

Pada titik ini Anda memiliki dua opsi juga:
Lakukan iterasi di atas $sfp_post_idsarray dengan sederhana foreachdan tarik data posting dengan satu get_post()per satu di dalam loop itu, atau, jika Anda ingin kebaikan dari WP_Query- paging, templat tag dan sebagainya - umpan $sfp_post_idske post__inparameter seperti dalam Solusi 1.

Johannes Pille
sumber
2

Ini semua terjadi karena ORhubungan meta_querydan cara WordPress menghasilkan string kueri yang sebenarnya. Saya akhirnya menghubungkan ke posts_clausesfilter untuk memodifikasi wheredan orderbypotongan-potongan dari pertanyaan:

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

Cukup tambahkan filter sebelum mengatur objek WP_Query Anda dan kemudian pastikan untuk menghapusnya setelah menjalankan kueri Anda untuk tidak memengaruhi kueri lain:

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

Ingatlah untuk meninggalkan meta_keydan orderbydari argumen permintaan.

Parham
sumber
1

Itu rumit :)

Saya akan menyarankan bahwa Anda mungkin tidak perlu memiliki array( 'key' => 'is_sponsored' )nilai dalam array 'meta_query' dan Anda bisa melakukan ini dengan menambahkan 'meta_key' ke array utama, tetapi sepertinya Anda sudah mencobanya. Apakah Anda mendapatkan hasil yang sama?

JOINIni dapat membuat segalanya menjadi rumit. Anda terhubung ke posts_orderby. Sudahkah Anda mempertimbangkan menghubungkan posts_fieldsdan menambahkan subquery yang akan memberi Anda meta_value?

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

s_ha_dum
sumber