カテゴリー
ブログ

一緒に付けられることが多いタグ(共起タグ)を可視化する

リスト投稿には1~5つのタグを付けるようにしています。同じタグが付いたリストは当然ながら性質が似ていて、類義語のようなタグが付くことが多くなります。

たとえば「選択」というタグが付けられたリスト投稿には「決断」というタグも付けられがち。このような関係にあるタグを「共起タグ」と命名しておきます。

共起タグを可視化したく、タグページの説明(description)欄に「このタグと共に使われていることが多いタグ」を付けてみました。

フィルターフックの箇所

TwentyTwenty テーマでは、タグページの説明は index.php から get_the_archive_description() を呼び出して表示しています。ですので、この関数内に用意されている ‘get_the_archive_description’ フィルターフックが使えます。

add_filter( 'get_the_archive_description', 'lf_tags_description', 10, 1 );
function lf_tags_description( $description ) {

    if( ! is_tag() ) {
        return $description;
    }

    /* 処理 */
}

上記の「処理」部分ではSQL文を使う場合と関数を使う場合の2通りのやり方を考えました。

SQL文を使う場合

「公開された」「投稿タイプがpostで」「listカテゴリーに属し」「表示しようとしているタグが付けられた」投稿群に付けられている他のタグを洗い出し、登場回数の多い順に5つを取り出すSQL文を書きました。

たとえば「選択」タグがつけられた公開リストは19あり、それぞれのリストには「決断」や「意思決定」などのタグも付けられています。それらの共起タグを重複ありで洗い出し、登場回数の多い順に5つ取り出すということ。

SELECT COUNT(t3.term_id) AS count, t3.term_id, t3.name
FROM wp_terms AS t
INNER JOIN wp_term_taxonomy AS tt ON t.term_id=tt.term_id AND taxonomy='post_tag'
INNER JOIN wp_term_relationships AS r ON tt. term_taxonomy_id=r.term_taxonomy_id
INNER JOIN wp_posts AS p ON r.object_id=p.ID AND p.post_status='publish' AND p.post_type='post'
INNER JOIN wp_term_relationships AS r2 ON p.ID=r2.object_id
INNER JOIN wp_term_taxonomy AS tt2 ON r2.term_taxonomy_id=tt2.term_taxonomy_id AND tt2.taxonomy='category'
INNER JOIN wp_terms AS t2 ON tt2.term_id=t2.term_id AND t2.name='list'
INNER JOIN wp_term_relationships AS r3 ON p.ID=r3.object_id
INNER JOIN wp_term_taxonomy AS tt3 ON r3.term_taxonomy_id=tt3.term_taxonomy_id AND tt3.taxonomy='post_tag'
INNER JOIN wp_terms AS t3 ON tt3.term_id=t3.term_id AND t3.term_id != (表示タグのID)
WHERE t.term_id=(表示タグのID)
GROUP BY t3.term_id
ORDER BY count DESC
LIMIT 5

このSQL文を発行し、得られた結果を下記のようにHTMLに組み立てて返します。

    $coused_tags = $wpdb->get_results( $sql );
    foreach( $coused_tags as $c ) {
        $desc[] = '<a href="'.get_tag_link( $c->term_id ).'" title="'.$c->count.'">'.$c->name.'</a>';
    }

    return 'このタグと共に使われていることが多いタグ<br>'.implode( ' ', $desc );

関数を使う場合

関数を使う場合はいくつかのステップが必要です。

1. 表示しようとしているタグが付けられている投稿群を取得する

$wp_query がタグの付いた投稿群を最初のページ分だけは保持しているので、ページングが無ければ $wp_query から投稿IDの一覧を取得する。ページングがあれば新規にクエリを発行して投稿IDの一覧を取得する。

    $tag_id = get_query_var( 'tag_id' );

    global $wp_query;

    if( $wp_query->max_num_pages == 1 ) {
        foreach( $wp_query->posts as $p ) {
            $post_ids[] = $p->ID;
        }
    } else {
        $q = new WP_Query( array(
            'post_type'      => 'post',
            'post_status'    => 'public',
            'category_name'  => 'list',
            'tag_id'         => $tag_id,
            'posts_per_page' => -1, // 全行
            'fields'         => 'ids', // IDのみ
        ) );
        $post_ids = $q->posts;
    }

2. それらの投稿群に付いている(当該タグ以外の)タグを「重複ありで」取得する

共起している回数を数えるため、上記の投稿群に付いているタグを重複ありで取得する必要があります。ここがわからなくてSQL文を書いたのですが、 wp_get_object_terms()関数がそのようなオプションを提供していました。

もし共起タグが見つからなければここで処理終了。

    $coused_tags_all = wp_get_object_terms( $post_ids, 'post_tag', array(
        'fields' => 'all_with_object_id',
        'exclude' => $tag_id,
    ) );

    if( ! count( $coused_tags_all ) ) {
        return $description;
    }

3. 共起タグの出現回数トップ5を取り出す

$coused_tags_all は Term オブジェクトの配列で、画面表示に必要なタグ名称を持っています。余計な呼び出しを減らすため、取得済みの Term オブジェクトをできるだけ活用。

$coused_tags はタグIDをキーとする配列で、登場回数とTermオブジェクトを値に持たせています。

// タグの出現回数を数える
$coused_tags = array();
foreach( $coused_tags_all as $c ) {
    if( ! isset( $coused_tags[$c->term_id] ) ) {
        $coused_tags[$c->term_id] = array( 'count' => 1, 'obj' => $c );
    } else {
        $coused_tags[$c->term_id]['count']++;
    }
}

// 出現回数の降順でソート
uasort ( $coused_tags , function ($a, $b) {
    return $b['count'] - $a['count'];
} );

// 最大5つを取り出す
$coused_tags = array_slice( $coused_tags, 0, 5, true );

4. 表示するHTMLを作成する

foreach( $coused_tags as $k => $v ) {
    $desc[] = '<a href="'.get_tag_link( $k ).'" title="'.$v['count'].'">'.$v['obj']->name.'</a>';
}

return '「'.urldecode( get_query_var( 'tag' ) ).'」と共に使われていることが多いタグ<br>'.implode( ' ', $desc );

「一緒に付けられることが多いタグ(共起タグ)を可視化する」への1件の返信

コメントを残す