Komplexe Metaabfrage mit 3 Schlüsseln

8

Ich denke, das Problem hängt im Wesentlichen mit der SQL-Abfragestruktur zusammen und ich bin kein Experte.

Ich muss nach Beiträgen (benutzerdefinierter Beitragstyp) anhand von 2 Parametern suchen:

  1. pd_city

  2. pd_country

Bitte beachten Sie, dass die meta_query-Beziehung 'ODER' ist. Wenn also eine der beiden oben genannten LIKE ist, sollten wir einige Ergebnisse erzielen.

Der dritte Schlüssel (is_sponsored) wird zum Sortieren von Posts verwendet! Es kann 1 oder 0 sein und Beiträge, deren Wert "is_sponsored" gleich 1 ist, sollten oben aufgeführt werden.

Also hier ist die WordPress-Sache:

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

Ich muss die Ergebnisse auch mit "posts_orderby" filtern, um die Sponsoren an die Spitze zu bringen:

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

Das eigentliche Problem besteht in der Tat darin, dass bei dieser Abfrage ALLE POSTS von "sfp_post_category" zurückgegeben werden, nicht nur diejenigen, die mit "pd_city" oder "pd_country" übereinstimmen, da ALLE POSTS "is_sponsored" -Meta-Schlüssel haben (und der Wert auf 1 oder 0 gesetzt ist). Noch einmal: "is_sponsored" wird zum Sortieren benötigt!

Wenn var_dump

var_dump( $sfp_search->request );

... WordPress 'SQL sieht so aus:

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

Wie entferne ich alle Beiträge, die nicht mit "pd_city" oder "pd_country" übereinstimmen, aus den Ergebnissen?

Dameer
quelle

Antworten:

6

Der Täter

Der Schuldige der Sache sind Meta-Abfragen, die keine unterschiedlichen und / oder verschachtelten Beziehungen unterstützen - ein Mangel, der mich übrigens schon früher verrückt gemacht hat. In einer aktuellen Instanz mit einem Suchszenario auch.

Was Sie tun möchten, kann einfach nicht mit WP_Queryeiner Schleife allein erreicht werden.
Wie Sie anscheinend bemerkt haben, meta_queryspielt es keine Rolle , ob Sie den Sortierschlüssel als allgemeines Abfrageargument im Array oder außerhalb des Arrays platzieren. Wenn Sie die Metaabfragebeziehung auf OReine meta_keybeliebige Stelle der Abfrageargumente festlegen und diese angeben, ohne den zugehörigen meta_valueParameter festzulegen , gibt die Abfrage immer mindestens alle Beiträge zurück, in denen dieser meta_key festgelegt ist.
Übrigens und der Vollständigkeit halber: Wenn Sie eine einzelne meta_query mit !=als Wert für verwenden meta_compare, gibt die Abfrage alle Ergebnisse mit der meta_keyMenge zurück und ist nicht gleich der angegebenen meta_value- dies ist nicht der FallGeben Sie alle Beiträge zurück, die überhaupt nicht meta_keyverwendet wurden. Ein weiterer Punkt, an dem Metaabfragen fehlschlagen.

Lösung 1

Ich sehe zwei Möglichkeiten. Zum einen können Sie den is_sponsoredMetaschlüssel in der Abfrage weglassen, auch die Paginierung weglassen, die richtigen Beiträge abrufen und die Sortierung mit einer zweiten Instanz von durchführen WP_Queryund die gefilterten Beitrags-IDs über den post__inParameter übergeben:

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

Beachten Sie, dass der $orderbyParameter von WP_Querymehrere Werte annimmt, die durch ein Leerzeichen getrennt sind. Ihre Suchänderung ist möglicherweise komplexer als erforderlich.

Lösung 2

Da mir Ihre Idee gefällt, die Eigenschaft des Abfrageobjekts mit var_dumping zu versehen, möchte requestich einen kurzen - und nicht getesteten - sekundären Vorschlag auslösen :

Wenn Sie die angegebene SQL geringfügig geändert haben, indem Sie den logischen Operator von OR mt2.meta_key = 'is_sponsored'in geändert ANDund entsprechend verschoben haben, können Sie die Beiträge mit $wpdbfolgenden Elementen abrufen :

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

An dieser Stelle haben Sie auch zwei Möglichkeiten:
Entweder $sfp_post_idsmit einem einfachen Befehl über das Array iterieren foreachund die Post-Daten get_post()einzeln innerhalb dieser Schleife abrufen, oder, wenn Sie die Feinheiten von WP_QueryPaging, Vorlagen-Tags usw. wünschen $sfp_post_ids, dem post__inParameter zuführen wie in Lösung 1.

Johannes Pille
quelle
2

Dies alles geschieht aufgrund der ORBeziehung meta_queryund der Art und Weise, wie WordPress die eigentliche Abfragezeichenfolge generiert. Am Ende habe ich mich in den posts_clausesFilter eingehakt, um die whereund orderbyTeile der Abfrage zu ändern :

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

Fügen Sie einfach den Filter hinzu, bevor Sie Ihr WP_Query-Objekt einrichten, und entfernen Sie ihn nach dem Ausführen Ihrer Abfrage, um andere Abfragen nicht zu beeinträchtigen:

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

Denken Sie daran, die meta_keyund orderbyaus den Abfrage-Argumenten wegzulassen.

Parham
quelle
1

Das ist kompliziert :)

Ich wollte vorschlagen, dass Sie den array( 'key' => 'is_sponsored' )Wert möglicherweise nicht im Array 'meta_query' haben müssen und dass Sie dies tun können, indem Sie dem Hauptarray einen 'meta_key' hinzufügen, aber es sieht so aus, als hätten Sie das versucht. Haben Sie die gleichen Ergebnisse erzielt?

JOINs kann die Dinge komplizieren. Du hast dich eingehakt posts_orderby. Haben Sie darüber nachgedacht, posts_fieldseine Unterabfrage einzuhängen und hinzuzufügen, mit der Sie Ihren meta_value erhalten?

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

s_ha_dum
quelle