Better way to get tag stats?

时间:2015-04-22 作者:user1437251

我有4000多个帖子。我正在尝试查询所有帖子,获取每个帖子的标签数量,并根据帖子在仪表板中的标签数量总结帖子数量。当post\\u per\\u page小于2000但超过2000时,posts计数会正确显示,查询超时。它只显示所有的“0”。

密码

  $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\';
是否有更好的方法来查询此信息,以便不发生超时?

3 个回复
最合适的回答,由SO网友:TheDeadMedic 整理而成

我的地址是a similar problem 不久前,一切都在记忆中:

$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_tagsget_object_term_cache - 否则我们将失去所有的努力!(前几首热门歌曲get_post, 它将在每次迭代时命中db,并将post对象放入内存中-props@Pieter Goosen)。

Update 2: 的第二个参数update_object_term_cache 应该是post类型,而不是分类法。

SO网友:Pieter Goosen

你正在以每小时500英里的飓风袭击db,难怪你的查询超时了。

这里有一两个加快速度的想法

添加\'fields\' => \'ids\', 到您的WP_Query 参数。这将大大加快您的查询速度。这将只返回帖子id,这是您实际需要的唯一内容

使用wp_get_post_terms() 获取帖子标签。第三个参数接受一个参数数组,一个beignfields 也可以设置为只返回ids 这也会加快查询速度,因为它只返回标记ID,而不是完整的标记对象

发布新帖子或删除、取消删除或更新帖子时,使用瞬态保存结果并刷新它们。使用transition_post_status

如果发布了新帖子,或者删除、取消删除或更新了帖子,请设置此功能以删除该帖子

在您的功能中。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_%\')" );
});
获取标记计数并将其添加到瞬态

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;
}
您可以在模板中使用以下函数

$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>\';
}
一些重要注意事项上面的代码未经测试,可能有缺陷

需要PHP 5.4+

第一个参数是$taxonomy. 您可以将任何分类法传递给代码,deafult是post_tag. 第二个参数是$post_type 设置为默认值post. 可以将任何post类型传递给参数

根据需要修改和滥用

编辑1

修复了几个小错误,代码现在已经过测试,可以正常工作了

编辑2-性能测试-报废---

编辑3多亏了@TheDeadMedic,我还从@TheDeadMedic学到了一些关于update_object_term_cacheget_object_term_cache 这大大提高了性能。我已经用这些信息更新了我的答案(从@TheDeadMedic那里偷了一点,作为回报,我对他的答案投了赞成票:-))。它确实会对内存造成一些影响,但使用瞬态可以部分克服这个问题。

代码现在给了我

0.09766秒内查询2次。

不使用瞬态

SO网友:birgire

频率表-自定义SQL查询:

您可以尝试以下自定义查询,以获取给定分类法、帖子状态和类型的帖子/术语统计信息:

/**
 * 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 
    );
}
安装约10k个帖子的示例:以下是已发布帖子中类别的示例:

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

print_r( $stats );
具有以下输出:

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
        )

)
我们可以将其输出到HTML表中:

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 );
具有以下输出:

stats

所以在这里,我们可以看到有多少帖子有一定数量的术语,按顺序排列。

目前,SQL查询使用临时和文件排序,因此肯定有机会对其进行调整。在10k POST安装上,这在我的小型VPS上运行不到0.2秒。例如,我们可以删除post表连接以使其更快,但这样会变得不太灵活。

我们还可以缓存输出,例如使用@Pieter Goosen在回答中提到的瞬态API。

结束

相关推荐

Get posts with no tags?

我有1000多篇没有标签的帖子。基本上,我尝试在前端显示没有标签的帖子,以便用户可以从前端添加标签。我在循环中使用这种方法,以便显示没有标签的帖子。<?php $tag = get_the_tags(); if (! $tag) { ?> <a href=\"<?php the_permalink();?>\"><?php the_title() ?></a><br>