3つのキーを持つ複雑なメタクエリ


8

問題は本質的にSQLクエリ構造に関連していると思います。私はエキスパートではありません...

2つのパラメーターで投稿(カスタム投稿タイプ)を検索する必要があります。

  1. pd_city

  2. pd_country

meta_query関係は「OR」であるため、上記の2つのいずれかがLIKEの場合、結果が得られることに注意してください。

3番目のキー(is_sponsored)は投稿のソートに使用されます!1または0にすることができ、 "is_sponsored"の値が1に等しい投稿が一番上に表示されます。

だからここに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 );

また、上位にスポンサーを得るために、「posts_orderby」で結果をフィルタリングする必要があります。

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

実際の問題は、このクエリでは「sfp_post_category」からのすべての投稿が返されることを実際に当てはめます。もう一度:並べ替えには "is_sponsored"が必要です!

var_dump

var_dump( $sfp_search->request );

... WordPressのsqlは次のようになります。

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

「pd_city」または「pd_country」に一致しないすべての投稿を結果から除外するにはどうすればよいですか?

回答:


6

犯人

問題の原因は、メタクエリが異なるリレーションやネストされたリレーションをサポートしていないことです。最近の例では、検索シナリオもあります。

あなたがしたいことはWP_Query、1つのループだけでは達成できません。
お気づきのように、並べ替えキーをmeta_query配列内に配置するか、配列外に配置するかは、一般的なクエリ引数としては変わりません。付随するパラメーターを設定せずにメタクエリリレーションを設定してクエリ引数の任意の場所をOR指定すると、クエリは常に少なくともそのmeta_keyが設定されているすべての投稿を返します。ちなみにおよび完全を期すために:あなたが単一meta_queryを使用した場合の値として、クエリが持つすべての結果を返しますセットと与えられたに等しいではない-それは意志ではありませんmeta_keymeta_value
!=meta_comparemeta_keymeta_valueまったくmeta_key使用されていない投稿を返します。メタクエリが失敗するもう1つのポイント。

解決策1

2つのオプションが表示されます。1つはis_sponsored、クエリからメタキーを省略し、ページネーションも省略し、正しい投稿を取得して、の2番目のインスタンスで並べ替えを行いWP_Query、フィルターされた投稿IDをpost__inパラメーターを介して渡します。

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

$orderbyパラメータはWP_Query、スペースで区切られた複数の値を取ることに注意してください。検索の変更は、必要以上に複雑になる場合があります。

解決策2

クエリオブジェクトのrequestプロパティをvar_dumpするというあなたのアイデアが気に入ったので、すぐに、そしてテストされていないことに注意してください。

の論理演算子をOR mt2.meta_key = 'is_sponsored'に変更し、ANDそれに応じてSQLをわずかに変更した場合、次のようにして投稿をプルできます$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
    "
);

この時点で2つのオプションもあります。単純な配列で配列を
反復処理し、そのループ内で個別に投稿データをプルするか、またはページング、テンプレートタグなどの優れた機能が必要な場合は、パラメーターにフィードします。ソリューション1と同様。$sfp_post_idsforeachget_post()WP_Query$sfp_post_idspost__in


2

これはすべて、WordPressが実際のクエリ文字列を生成する方法とそのOR関係が原因でmeta_query発生します。クエリのおよび部分posts_clausesを変更するためにフィルターにフックすることにwhereなりましたorderby

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

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

meta_keyorderbyクエリ引数から除外することを忘れないでください。


1

それは複雑です:)

array( 'key' => 'is_sponsored' )'meta_query'配列に値を含める必要がない場合や、メイン配列に 'meta_key'を追加することでこれを実現できることをお勧めしますが、試したようです。同じ結果が得られましたか?

JOINsは物事を複雑にする可能性があります。に夢中になりましたposts_orderbyposts_fieldsmeta_valueを取得するサブクエリにフックして追加することを検討しましたか?

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.