Setze Alias ​​für meta_query Argumente in get_posts ()

Gibt es eine Möglichkeit, bei der Ausführung von get_posts() einen Alias ​​für die Meta-Abfrage-Argumente get_posts() ? Eine meiner Abfragen funktioniert schlecht. Um zu optimieren, muss ich nur in der Lage sein, die gleiche verbundene Tabelle wiederzuverwenden, anstatt 3 Tabellen zu verbinden, wenn nur eine benötigt wird.

Mein aktuelles Beispiel …

 $args = array( 'meta_query' => array( 'relation' => 'AND', array( 'key' => 'abc_type', 'value' => array('puppy', 'kitten'), 'compare' => 'IN', ), array( 'relation' => 'OR', array( 'relation' => 'AND', array( 'key' => 'abc_type', 'value' => 'puppy', 'compare' => '=', ), array( 'key' => 'abc_color', 'value' => 'pink', 'compare' => '=', ), ), array( 'relation' => 'AND', array( 'key' => 'abc_type', 'value' => 'kitten', 'compare' => '=', ), array( 'key' => 'abc_size', 'value' => 'large', 'compare' => '=', ), ), ), ) ); get_posts($args); 

was im Grunde in geraden SQL übersetzt …

 SELECT posts.* FROM posts INNER JOIN postmeta ON ( posts.ID = postmeta.post_id ) INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id ) INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id ) INNER JOIN postmeta AS mt3 ON ( posts.ID = mt3.post_id ) WHERE 1=1 AND ( ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') ) AND ( ( ( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' ) AND ( mt2.meta_key = 'abc_color' AND mt2.meta_value > 'pink' ) ) OR ( ( mt3.meta_key = 'abc_type' AND mt3.meta_value = 'kitten' ) AND ( mt4.meta_key = 'abc_size' AND mt4.meta_value = 'large' ) ) ) ) AND posts.post_type = 'abc_mypost' AND ((posts.post_status = 'publish')) GROUP BY posts.ID ORDER BY posts.post_title ASC; 

Dies fügt jedoch zwei zusätzliche Joins für das benutzerdefinierte abc_type und da diese performance einen großen Erfolg hatte. Gibt es eine Möglichkeit, denselben Alias ​​für mehrere Meta-Query-Argumente referenzieren zu können? Im Grunde sind mt1 und mt3 völlig unnötig, ich sollte nur in der Lage sein, die erste postmeta Tabelle zu referenzieren, die mit der ersten verwendet wird ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') ) . Oder zumindest, wenn ich einen benutzerdefinierten Alias ​​für jeden von diesen festlegen kann, könnte ich darauf verweisen.

Eine optimale Abfrage wäre …

 SELECT posts.* FROM posts INNER JOIN postmeta ON ( posts.ID = postmeta.post_id ) INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id ) INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id ) WHERE 1=1 AND ( ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value IN ('puppy','kitten') ) AND ( ( ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'puppy' ) AND ( mt1.meta_key = 'abc_color' AND mt1.meta_value > 'pink' ) ) OR ( ( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'kitten' ) AND ( mt2.meta_key = 'abc_color' AND mt2.meta_value = 'green' ) ) ) ) AND posts.post_type = 'abc_mypost' AND ((posts.post_status = 'publish')) GROUP BY posts.ID ORDER BY posts.post_title ASC; 

Gedanken?

Solutions Collecting From Web of "Setze Alias ​​für meta_query Argumente in get_posts ()"

Sie können die Filter posts_where und posts_join verwenden, um die Abfrage zu ändern. Es ist nicht sehr elegant, aber Sie sollten in der Lage sein, mit diesen beiden Filtern zu stören, so dass Ihre SQL optimiert ist. Es ist eine Art Brute-Forcey, aber ich kann keinen besseren Weg in der WP_Query-class sehen. Das heißt nicht, dass es nicht ist.

 //* Make sure to not suppress filters $args = array( 'suppress_filters' => false, //* rest of args unchanged ); add_filter( 'posts_where', function( $sql ) { $sql = str_replace( "( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' )", "( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'puppy' )", $sql ); $sql = str_replace( "( mt3.meta_key = 'abc_type' AND mt3.meta_value = 'kitten' )", "( postmeta.meta_key = 'abc_type' AND postmeta.meta_value = 'kitten' )", $sql ); $sql = str_replace( [ 'mt2', 'mt4' ], [ 'mt1', 'mt2' ], $sql ); return $sql; }); add_filter( 'posts_join', function( $sql ) { $sql = str_replace( " INNER JOIN wp_postmeta AS mt4 ON ( wp_posts.ID = mt4.post_id )", "", $sql ); $sql = str_replace( " INNER JOIN wp_postmeta AS mt3 ON ( wp_posts.ID = mt3.post_id )", "", $sql ); return $sql; }); 

Dort sollten wahrscheinlich einige Prüfungen vorhanden sein, damit Sie nicht versehentlich andere Abfragen ändern. Das ist eine Übung für den Leser.

meta_query_find_compatible_table_alias Filter meta_query_find_compatible_table_alias an, der in wp-includes/class-wp-meta-query.php . Die Dokumentation dieses Filters:

 /** * Filters the table alias identified as compatible with the current clause. * * @since 4.1.0 * * @param string|bool $alias Table alias, or false if none was found. * @param array $clause First-order query clause. * @param array $parent_query Parent of $clause. * @param object $this WP_Meta_Query object. */ return apply_filters( 'meta_query_find_compatible_table_alias', $alias, $clause, $parent_query, $this ); 

Es ist wahrscheinlich, dass die aufrufende function find_compatible_table_alias false find_compatible_table_alias und somit die Abfrage die mt* Aliase erzeugt. Hier ist ein Beispielcode, der diesen Filter verwendet, obwohl ich mich persönlich für etwas einstelle, das ein wenig einfacher zu verstehen ist. Das Ändern von Abfragen wie dieser kann zu einer Menge Kopfschmerzen führen und es ist möglicherweise gar nicht ersichtlich, wo die Abfrage durcheinander gerät, besonders wenn Sie in Zukunft andere Entwickler hinzuziehen. Das gesagt…

 // Reuse the same alias for the abc_type meta key. function pets_modify_meta_query( $alias, $meta_query ) { if ( 'abc_type' === $meta_query['key'] ) { return 'mt1'; } return $alias; } // Filter the query. add_filter( 'meta_query_find_compatible_table_alias', 'pets_modify_meta_query', 10, 2 ); $args = array( 'meta_query' => array( 'relation' => 'AND', array( 'key' => 'abc_type', 'value' => array('puppy', 'kitten'), 'compare' => 'IN', ), array( 'relation' => 'OR', array( 'relation' => 'AND', array( 'key' => 'abc_type', 'value' => 'puppy', 'compare' => '=', ), array( 'key' => 'abc_color', 'value' => 'pink', 'compare' => '=', ), ), array( 'relation' => 'AND', array( 'key' => 'abc_type', 'value' => 'kitten', 'compare' => '=', ), array( 'key' => 'abc_size', 'value' => 'large', 'compare' => '=', ), ), ), ) ); $q = new WP_Query($args); echo '
', print_r($q->request, true); die;

Dies führt zu einer Abfrage wie

 SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts 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 ) WHERE 1=1 AND ( ( mt1.meta_key = 'abc_type' AND mt1.meta_value IN ('puppy','kitten') ) AND ( ( ( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'puppy' ) AND ( wp_postmeta.meta_key = 'abc_color' AND wp_postmeta.meta_value = 'pink' ) ) OR ( ( mt1.meta_key = 'abc_type' AND mt1.meta_value = 'kitten' ) AND ( mt1.meta_key = 'abc_size' AND mt1.meta_value = 'large' ) ) ) ) AND wp_posts.post_type = 'post' AND ( wp_posts.post_status = 'publish' OR wp_posts.post_status = 'future' OR wp_posts.post_status = 'draft' OR wp_posts.post_status = 'pending' ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10 

Sie können Ihre Abfrage optimieren, indem Sie die erste Meta-Abfrage entfernen, da diese überflüssig ist:

 $args = array( 'meta_query' => array( 'relation' => 'OR', array( 'relation' => 'AND', array( 'key' => 'abc_type', 'value' => 'puppy', 'compare' => '=', ), array( 'key' => 'abc_color', 'value' => 'pink', 'compare' => '=', ), ), array( 'relation' => 'AND', array( 'key' => 'abc_type', 'value' => 'kitten', 'compare' => '=', ), array( 'key' => 'abc_size', 'value' => 'large', 'compare' => '=', ), ), ), ); get_posts($args); 

Auf diese Weise werden Sie entweder nur einen pink puppy oder ein large kitten , wie Sie meinen, glaube ich.

Was die Optimierung der internen MySQL-Abfragen von WordPress betrifft, sollten Sie sich meiner Meinung nach davon freihalten, da Sie sich möglichen Nebenwirkungen aussetzen würden. Sie sollten sich besser darauf verlassen, dass Abfragen zwischengespeichert werden und mehr PHP-Verarbeitung für den (größeren) Datensatz durchführen. Ich glaube, dies wird insgesamt zu einer besseren Performance führen, da der Flaschenhals nicht die Menge an Daten ist, die Sie aus der database extrahieren, sondern die Schwierigkeit, mit der er gesammelt wird (wie viele Abfragen). PHP ist ziemlich schnell, da es durch Arrays kommt.

Ich glaube, dass eine Situation wie diese schneller ist, wenn man bedenkt, dass das Post-Meta zwischengespeichert wird:

 $args = array( 'meta_query' => array( array( 'key' => 'abc_type', 'value' => array('puppy', 'kitten'), 'compare' => 'IN', ), ), ); $final_posts = array(); foreach( $get_posts($args) as $post ) { if ( 'puppy' === get_post_meta( $post->ID, 'abc_type', true ) ) { if ( 'pink' === get_post_meta( $post->ID, 'abc_color', true ) ) { $final_posts[] = $post; } } else { // This is definitely a kitten if ( 'large' === get_post_meta( $post->ID, 'abc_size', true ) ) { $final_posts[] = $post; } } } 

Ich bin nicht wirklich ein database-Typ, aber ich habe einmal im Fernsehen gespielt …

Wäre das nicht Teil

 SELECT posts.* FROM posts INNER JOIN postmeta ON ( posts.ID = postmeta.post_id ) INNER JOIN postmeta AS mt1 ON ( posts.ID = mt1.post_id ) INNER JOIN postmeta AS mt2 ON ( posts.ID = mt2.post_id ) 

besser ersetzt werden mit

 SELECT posts.* FROM posts INNER JOIN postmeta ON (( posts.ID = postmeta.post_id ) and ( posts.ID = mt1.post_id ) and ( posts.ID = mt2.post_id )) 

Das könnte wahrscheinlich noch mehr vereinfacht werden … mit einem Alias, der dort an der richtigen Stelle steckte, so dass Sie den Rest Ihrer Abfrage verwenden könnten.

Nur ein Gedanke…