Truy vấn meta phức tạp với 3 khóa


8

Tôi nghĩ rằng vấn đề chủ yếu liên quan đến cấu trúc truy vấn sql và tôi không phải là một chuyên gia ....

Tôi cần tìm kiếm bài viết (loại bài tùy chỉnh) theo 2 tham số:

  1. pd_city

  2. pd_country

Xin lưu ý rằng mối quan hệ meta_query là 'HOẶC' vì vậy nếu một trong hai điều trên là THÍCH thì chúng ta sẽ có một số kết quả.

Khóa thứ ba (được tài trợ) được sử dụng để sắp xếp bài viết! Nó có thể là 1 hoặc 0 và các bài đăng có giá trị "được tài trợ" bằng 1 nên được liệt kê ở trên cùng.

Vì vậy, đây là điều 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 );

Tôi cũng cần lọc kết quả với "post_orderby" để đưa những người được tài trợ lên đầu:

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

Vấn đề thực sự phụ thuộc vào thực tế là với truy vấn này, TẤT CẢ BÀI VIẾT từ "sfp_post_carget" được trả về, không chỉ những người khớp với "pd_city" hoặc "pd_country" vì TẤT CẢ BÀI VIẾT CÓ "được tài trợ" (và giá trị được đặt thành 1 hoặc 0). Một lần nữa: "được tài trợ" là cần thiết để sắp xếp!

Khi var_dump

var_dump( $sfp_search->request );

... sql của WordPress trông như thế này:

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

Làm cách nào để loại bỏ tất cả các bài đăng không khớp với "pd_city" hoặc "pd_country" khỏi kết quả?

Câu trả lời:


6

Thủ phạm

Thủ phạm của vấn đề là các truy vấn meta không hỗ trợ các mối quan hệ khác nhau và / hoặc lồng nhau - một sự thiếu sót bằng cách này, điều đó cũng khiến tôi phát điên. Trong một trường hợp gần đây với một kịch bản tìm kiếm cũng.

Những gì bạn muốn làm chỉ đơn giản là không thể được thực hiện WP_Querycũng như một vòng lặp.
Như bạn có vẻ đã nhận thấy, việc bạn đặt khóa sắp xếp trong meta_querymảng hay bên ngoài nó như một đối số truy vấn chung sẽ không tạo ra sự khác biệt. Nếu bạn đặt quan hệ truy vấn meta thành ORvà chỉ định một vị trí meta_keycủa truy vấn mà không đặt meta_valuetham số đi kèm , truy vấn sẽ luôn trả về ít nhất tất cả các bài đăng nơi meta_key được đặt.
Nhân tiện và vì mục đích hoàn chỉnh: Khi bạn sử dụng một meta_query duy nhất với !=giá trị meta_compare, truy vấn sẽ trả về tất cả các kết quả với meta_keytập hợp và không bằng giá trị đã cho meta_value- nó sẽ khôngtrả lại bất kỳ bài viết nào không meta_keyđược sử dụng. Một điểm khác mà các truy vấn meta không thành công.

Giải pháp 1

Tôi thấy hai lựa chọn. Đối với một, bạn có thể bỏ qua is_sponsoredkhóa meta khỏi truy vấn, cũng bỏ qua phân trang, nhận các bài đăng chính xác và thực hiện sắp xếp với một phiên bản thứ hai WP_Query, chuyển ID bài đăng được lọc qua post__intham số:

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

Lưu ý rằng tham $orderbysố WP_Querysẽ lấy nhiều giá trị cách nhau bởi một khoảng trắng. Sửa đổi tìm kiếm của bạn có thể phức tạp hơn cần thiết.

Giải pháp 2

Vì tôi thích ý tưởng của bạn về việc thay đổi thuộc tính của đối tượng truy vấn request, hãy để tôi đưa ra một gợi ý phụ - và, chưa được kiểm tra - chưa được kiểm tra:

Nếu bạn sửa đổi một chút SQL đã cho bằng cách thay đổi toán tử logic OR mt2.meta_key = 'is_sponsored'thành ANDvà di chuyển nó cho phù hợp, bạn có thể kéo các bài đăng bằng $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
    "
);

Tại thời điểm này, bạn có hai lựa chọn cũng như:
Hoặc là lặp qua $sfp_post_idsmảng với một đơn giản foreachvà kéo dữ liệu bài với get_post()cá nhân trong vòng lặp đó, hoặc, nếu bạn muốn niceties của WP_Query- phân trang, mẫu thẻ và vân vân - thức ăn $sfp_post_idscho post__intham số như trong Giải pháp 1.


2

Tất cả điều này xảy ra do ORmối quan hệ trên meta_queryvà cách WordPress tạo ra chuỗi truy vấn thực tế. Cuối cùng tôi đã nối vào posts_clausesbộ lọc để sửa đổi whereorderbycác phần của truy vấn:

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

Chỉ cần thêm bộ lọc trước khi thiết lập đối tượng WP_Query của bạn và sau đó đảm bảo xóa nó sau khi chạy truy vấn của bạn để không ảnh hưởng đến các truy vấn khác:

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

Hãy nhớ để lại meta_keyorderbytừ truy vấn args.


1

Điều đó thật phức tạp :)

Tôi sẽ đề nghị rằng bạn có thể không cần phải có array( 'key' => 'is_sponsored' )giá trị trong mảng 'meta_query' và bạn có thể làm điều này bằng cách thêm 'meta_key' vào mảng chính, nhưng có vẻ như bạn đã thử điều đó. Bạn đã nhận được kết quả tương tự?

JOINs có thể làm cho mọi thứ phức tạp. Bạn móc vào posts_orderby. Bạn đã xem xét việc kết nối posts_fieldsvà thêm một truy vấn con sẽ giúp bạn có meta_value chưa?

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.