webentwicklung-frage-antwort-db.com.de

Besserer Weg, um Tag-Statistiken zu bekommen?

Ich habe über 4000 Beiträge. Ich versuche, alle Beiträge abzufragen und die Anzahl der Tags zu ermitteln, die jeder Beitrag enthält, und die Anzahl der Beiträge basierend auf der Anzahl der Tags, die der Beitrag im Dashboard enthält, zusammenzufassen. Die Anzahl der Posts wird ordnungsgemäß angezeigt, wenn post_per_page kleiner als 2000 ist, aber nach 2000 das Zeitlimit für die Abfrage überschritten wird. Es wird nur '0' für alle angezeigt.

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) {
       $morethantwo++;
     }
     if ($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 eine bessere Methode, dies abzufragen, damit das Timeout nicht auftritt?

4
user1437251

Ich habe ein ähnliches Problem vor nicht allzu langer Zeit angesprochen - alles im Gedächtnis:

$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:get_the_tags auf get_object_term_cache umgestellt - sonst verlieren wir alle harte Arbeit! (Der frühere Treffer get_post, der bei jeder Iteration die Datenbank trifft und das Post-Objekt in den Speicher schreibt - Requisiten @Pieter Goosen).

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

6
TheDeadMedic

Sie erreichen die Datenbank mit einem Hurrikan von 500 Meilen pro Stunde. Kein Wunder, dass bei Ihrer Abfrage eine Zeitüberschreitung auftritt.

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

  • Fügen Sie 'fields' => 'ids', zu Ihren WP_Query-Argumenten hinzu. Dadurch wird Ihre Abfrage erheblich beschleunigt. Dies gibt nur die Beitrags-IDs zurück, und dies ist das einzige, was Sie tatsächlich benötigen

  • Verwenden Sie wp_get_post_terms(), um die Post-Tags abzurufen. Der dritte Parameter nimmt ein Array von Argumenten an, eines davon ist fieldsname__, das Sie auch so einstellen können, dass es nur idszurückgibt, was auch Ihre Abfrage beschleunigt, da es auch nur Tag-IDs und nicht das gesamte Tag-Objekt zurückgibt

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

EDIT- IDEA CODE TRANSITION

Richten Sie die Funktion zum Löschen des temporären Ordners ein, wenn ein neuer Beitrag veröffentlicht wird oder wenn ein Beitrag gelöscht oder wiederhergestellt oder aktualisiert wird

In deiner functions.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_%')" );
});

Rufen Sie die Tag-Anzahl ab 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 Funktion in Ihrer Vorlage wie folgt verwenden

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

WICHTIGE HINWEISE

  • Der obige Code ist ungetestet und möglicherweise fehlerhaft

  • Benötigt PHP 5.4 +

  • Der erste Parameter ist $taxonomy. Sie können dem Code jede Taxonomie übergeben. Der Standardwert ist post_tag. Der zweite Parameter ist $post_type, der auf den Standardwert postfestgelegt ist. Sie können dem Parameter einen beliebigen Beitragstyp übergeben

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

EDIT 1

Es wurden einige kleinere Fehler behoben. Der Code wurde nun getestet und funktioniert

EDIT 2 - LEISTUNGSPRÜFUNG

--- ABGEKRATZT ---

EDIT 3 dank @TheDeadMedic

Ich habe auch ein bisschen von @TheDeadMedic über update_object_term_cache und get_object_term_cache gelernt, was die Leistung erheblich steigert. Ich habe aktualisiert (habe ein bisschen von @TheDeadMedic gestohlen, habe seine Antwort im Gegenzug hochgestuft: -)) meine Antwort mit dieser Information. Es trifft den Speicher zwar ein wenig, aber dieses Problem wird teilweise durch die Verwendung von Transienten überwunden.

Der Code gibt mir jetzt

2 Abfragen in 0.09766 Sekunden.

ohne Transienten zu verwenden

5
Pieter Goosen

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 Post-Status und einen bestimmten Typ ausführen:

/**
 * 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 ~ 10.000 Beiträgen:

Hier ist ein Beispiel für Kategorie in veröffentlichte Beiträge :

$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( 
        "<tr><td>%d</td><td>%d</td></tr>", 
        $row['total'], 
        $row['terms_per_post'] 
    );
}
printf( "<table><tr><th>#Posts</th><th>#Terms</th></tr>%s</table>", $rows );

mit folgender Ausgabe:

stats

Hier können wir also sehen, wie viele Beiträge eine bestimmte Anzahl von Begriffen in der richtigen Reihenfolge haben.

Momentan verwendet die SQL-Abfrage temporäre und Dateisortierung, daher gibt es definitiv Möglichkeiten, sie anzupassen. Bei einer Installation von 10.000 Posts dauerte es weniger als 0,2 Sekunden, bis sie auf meinem kleinen VPS lief. Wir könnten zum Beispiel den Beitragstabellen-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 transients API, wie von @Pieter Goosen in seiner Antwort erwähnt.

2
birgire