Bessere Möglichkeit, Tag-Statistiken zu erhalten?

Ich habe über 4000 Posts. Ich versuche, alle Beiträge abzufragen und die Anzahl der Tags zu erhalten, die jeder Post hat, und die Anzahl der Beiträge zusammenzufassen, basierend auf der Anzahl der Tags, die der Beitrag im Dashboard hat. Die Anzahl der Posts wird korrekt angezeigt, wenn post_per_page kleiner als 2000 ist, aber nach 2000 das Abfrage-Timeout. Es zeigt nur “0” für alle.

Code

$args = array( 'posts_per_page' => 4000, 'post_status' => 'publish', ); $zerotags = 0; $onetag = 0; $twotags = 0; $morethantwo = 0; $sixtags_plus = 0; $query = new WP_Query( $args ); while ( $query->have_posts() ) : $query->the_post(); $posttags = get_the_tags(); $tag_count = 0; foreach($posttags as $tag) { $tag_count++; } if ($tag_count == 0) { $zerotags++; } if ($tag_count == 1) { $onetag++; } if ($tag_count == 2) { $twotags++; } if ($tag_count > 2 && $tag_count = 6) { $sixtags_plus++; } endwhile; echo 'Zero Tags : '.$zerotags.'posts'; echo 'One Tag : '.$onetag.'posts'; echo 'Two Tags : '.$twotags.'posts'; echo 'More than 2 and less than 6 : '.$morethantwo.'posts'; echo 'More than 6 tags : '.$sixtags_plus.'posts'; 

Gibt es einen besseren Ansatz, dies abzufragen, so dass das Timeout nicht auftritt?

Solutions Collecting From Web of "Bessere Möglichkeit, Tag-Statistiken zu erhalten?"

Ich habe vor kurzem ein ähnliches Problem angesprochen – es ist alles in Erinnerung:

 $post_ids = get_posts( array( 'posts_per_page' => -1, 'post_status' => 'publish', 'fields' => 'ids', // Just grab IDs instead of pulling 1000's of objects into memory ) ); update_object_term_cache( $post_ids, 'post' ); // Cache all the post terms in one query, memory should be ok foreach ( $post_ids as $post_id ) { if ( ! $tags = get_object_term_cache( $post_id, 'post_tag' ) ) { $zerotags++; } else { $tag_count = count( $tags ); if ( $tag_count === 1 ) { $onetag++; } elseif ( $tag_count === 2 ) { $twotags++; } elseif ( $tag_count >= 6 ) { $sixtags_plus++; } if ( $tag_count > 2 && $tag_count < 6 ) { $morethantwo++; } } } 

Update: Geschaltete get_the_tags zu get_object_term_cache - sonst verlieren wir all unsere harte Arbeit! (Die get_post trifft get_post , die bei jeder Iteration auf die db trifft und das post-Objekt in den Speicher legt - reps @Pieter Goosen).

Update 2: Das zweite Argument für update_object_term_cache sollte der Post- Typ sein, nicht die Taxonomie.

Du hebst die db mit einem Hurrikan von 500 Meilen pro Stunde, kein Wunder, dass deine Anfrage abläuft.

Hier ist eine Idee oder zwei, um die Dinge zu beschleunigen

  • Fügen Sie Ihren WP_Query Argumenten 'fields' => 'ids', WP_Query . Dies wird Ihre Anfrage dramatisch beschleunigen. Dies wird nur die Post-IDs zurückgeben, und das ist das einzige, was Sie wirklich brauchen

  • Verwenden Sie wp_get_post_terms() , um die Post-Tags zu erhalten. Der dritte Parameter benötigt ein Array von Argumenten, ein beliebiges fields das Sie auch für die Rückgabe von ids was auch Ihre Abfrage beschleunigt, da es auch nur die Tag- ids zurückgibt und nicht das komplette Tag-Objekt

  • Verwenden Sie Transienten, um Ihre Ergebnisse zu speichern und zu leeren, wenn ein neuer Beitrag veröffentlicht wird oder wenn ein Beitrag gelöscht, wiederhergestellt oder aktualisiert wird. Verwenden Sie transition_post_status

BEARBEITEN – IDEE ZUM CODE-ÜBERGANG

Richten Sie die function ein, um das tansient zu löschen, wenn ein neuer Beitrag veröffentlicht wird oder wenn ein Beitrag gelöscht, wiederhergestellt oder aktualisiert wird

In deiner function.php

 add_action( 'transition_post_status', function () { global $wpdb; $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient%_tag_list_%')" ); $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient_timeout%_tag_list_%')" ); }); 

Holen Sie sich die Anzahl der Tags und fügen Sie sie einem Transienten hinzu

 function get_term_post_count( $taxonomy = 'post_tag', $post_type = 'post' ) { if ( false === ( $total_counts = get_transient( 'tag_list_' . md5( $taxonomy . $post_type ) ) ) ) { if ( !taxonomy_exists( $taxonomy ) ) return $total_counts = null; $args = [ 'nopaging' => true, //Gets all posts 'fields' => 'ids' ]; $q = new WP_Query( $args ); if ( empty( $q->posts ) ) return $total_counts = null; update_object_term_cache( $q->posts, $post_type ); foreach ( $q->posts as $single_post ) { $tags = get_object_term_cache( $single_post, $taxonomy ); if ( empty( $tags ) ) { $no_tags[] = $single_post; } else { $count = count( $tags ); if ( $count == 1 ) { $one[] = $single_post; } elseif ( $count == 2 ) { $two[] = $single_post; } elseif ( $count >= 3 && $count < = 6 ) { $more_than_two[] = $single_post; } elseif ( $count > 6 ) { $more_than_six[] = $single_post; } } } $total_counts = [ 'none' => isset( $no_tags ) ? ( (int) count( $no_tags ) ) : 0, 'one' => isset( $one ) ? ( (int) count( $one ) ) : 0, 'two' => isset( $two ) ? ( (int) count( $two ) ) : 0, 'more_than_two' => isset( $more_than_two ) ? ( (int) count( $more_than_two ) ) : 0, 'more_than_six' => isset( $more_than_six ) ? ( (int) count( $more_than_six) ) : 0 ]; set_transient( 'tag_list_' . md5( $taxonomy . $post_type ), $total_counts, 24 * HOUR_IN_SECONDS ); return $total_counts; } 

Sie können die function in Ihrer Vorlage wie folgt verwenden

 $q = get_term_post_count(); if ( $q !== null ) { echo 'Zero Tags : '.$q['none'].'posts '; echo 'One Tag : '.$q['one'].'posts '; echo 'Two Tags : '.$q['two'].'posts '; echo 'More than 2 and less than 6 : '.$q['more_than_two'].'posts '; echo 'More than 6 tags : '.$q['more_than_six'].'posts '; } 

Einige wichtige Hinweise

  • Der obige Code ist nicht getestet und möglicherweise errorshaft

  • Benötigt PHP 5.4 +

  • Der erste Parameter ist $taxonomy . Sie können jede Taxonomie an den Code übergeben, der Standard ist post_tag . Der zweite Parameter ist $post_type der auf den Standard- post . Sie können einen beliebigen Post-Typ an den Parameter übergeben

  • Modifizieren und missbrauchen, wie Sie es für richtig halten

BEARBEITEN 1

Ein paar kleinere Fehler wurden behoben, der Code ist nun getestet und funktioniert

EDIT 2 – LEISTUNGSPRÜFUNG

— VERSCHROTT —

EDIT 3 dank @TheDeadMedic

Ich habe auch ein wenig von @TheDeadMedic über update_object_term_cache und get_object_term_cache was die performance stark erhöht. Ich habe aktualisiert ( etwas von @TheDeadMedic geklaut, upvoted seine Antwort im Gegenzug 🙂 ) meine Antwort mit dieser Info. Es trifft die Erinnerung ein wenig, aber dieses Problem wird teilweise mit der Verwendung von Transienten überwunden.

Der Code gibt mir jetzt

2 Abfragen in 0,09766 Sekunden.

ohne Transienten zu verwenden

Häufigkeitstabelle – benutzerdefinierte SQL-Abfrage:

Sie können die folgende benutzerdefinierte Abfrage für Ihre Posts / Terms-Statistiken für eine bestimmte Taxonomie, einen bestimmten Poststatus und -typ verwenden:

 /** * Frequency data: Count how many posts have a given number of terms, * for a given post type, post status and taxonomy. * * @param string $taxonomy Taxonomy slug * @param string $post_status Post status (draft, publish, ...) * @param string $post_type Post type (post, page, ...) * @return array Array containing freq. data with 'total' (posts) and 'term' counts */ function get_post_terms_stats_wpse_184993( $taxonomy = 'post_tag', $post_status = 'publish', $post_type = 'post' ){ global $wpdb; $sql = " SELECT COUNT( s.terms_per_post ) as total, s.terms_per_post FROM ( SELECT COUNT( tr.object_id ) terms_per_post, tr.object_id FROM {$wpdb->term_relationships} tr LEFT JOIN {$wpdb->term_taxonomy} tt USING( term_taxonomy_id ) LEFT JOIN {$wpdb->posts} p ON p.ID = tr.object_id WHERE tt.taxonomy = '%s' AND p.post_status = '%s' AND p.post_type = '%s' GROUP BY tr.object_id ) as s GROUP by s.terms_per_post ORDER BY total DESC"; return $wpdb->get_results( $wpdb->prepare( $sql, $taxonomy, $post_status, $post_type ), ARRAY_A ); } 

Beispiel für eine Installation mit ~ 10k Posts:

Hier ein Beispiel für die Kategorie in veröffentlichten Posts :

 $stats = get_post_terms_stats_wpse_184993( $taxonomy = 'category', $post_status = 'publish', $post_type = 'post' ); print_r( $stats ); 

mit folgender Ausgabe:

 Array ( [0] => Array ( [total] => 8173 [terms_per_post] => 1 ) [1] => Array ( [total] => 948 [terms_per_post] => 2 ) [2] => Array ( [total] => 94 [terms_per_post] => 3 ) [3] => Array ( [total] => 2 [terms_per_post] => 4 ) [4] => Array ( [total] => 1 [terms_per_post] => 6 ) [5] => Array ( [total] => 1 [terms_per_post] => 8 ) ) 

Wir können es in einer HTML-Tabelle ausgeben:

 foreach( $stats as $row ) { $rows .= sprintf( "%d%d", $row['total'], $row['terms_per_post'] ); } printf( "%s
#Posts#Terms
", $rows );

mit folgender Ausgabe:

Statistiken

Hier können wir sehen, wie viele Beiträge eine bestimmte Anzahl von Begriffen haben.

Derzeit verwendet die SQL-Abfrage temporär und filesort, daher gibt es definitiv Möglichkeiten, sie anzupassen. Bei einer 10k Post Installation dauerte das unter 0.2s um auf meinem kleinen VPS zu laufen. Wir könnten zum Beispiel den Post-Tabellen-Join entfernen, um ihn schneller zu machen, aber dann wäre er weniger flexibel.

Wir können die Ausgabe auch zwischenspeichern, zum Beispiel mit der Transienten- API, wie von @Pieter Goosen in seiner Antwort erwähnt.