あるカテゴリーの公開投稿で使われている、あるタクソノミー下のタームの総数を数える


公開リストの中で引用している書籍の総数を数えた。

  • 公開リストとは
    • post_type が post
    • post_status が publish
    • カテゴリー が list
  • 引用している書籍とは
    • asin タクソノミー 下にあるターム

標準関数を組み合わせると次のような手順で取得できる。

$q = new WP_Query( array(
	'post_type'      => 'post',
	'category_name'  => 'list',
	'post_status'    => 'publish',
	'posts_per_page' => -1,
	'fields'         => 'ids',
	'tax_query' => array( // 不要だが母集団を小さくするため
		array( 'taxonomy' => 'asin',
			'operator' => 'EXISTS' ),
    ),
) );

// ids: 投稿に付けられたASINタームの総数(重複無し)
$terms = wp_get_object_terms( $q->posts, 'asin', [ 'fields' => 'ids' ]  );
$term_count = count( $terms );	

// all_with_object_id: 投稿に付けられたASINタームの総数(重複あり、延べ引用数)
$terms = wp_get_object_terms( $q->posts, 'asin', [ 'fields' => 'all_with_object_id' ]  );
$term_count_total = count( $terms );

ここで $term_count がASINターム数つまり本の数、$term_count_total が延べ引用数ということになる。

ただ、2つの数字を得るために千の要素を超える配列を3つも経由するのはスマートでない気がするので、SQL文で取得することを試みた。

asinタクソノミー下のタームが関連付けられている公開投稿を探し出し、その投稿がlistカテゴリ下にあることを確認しなければならない。

データベース構造(WordPress Codex 日本語版)を見ながら愚直に INNER JOIN していった。このSQL文の結果と上の関数の結果が同じで、いくつか書籍を絞ってテストしてみても正しい結果が取得できたので、きちんと動いている模様。

SELECT COUNT(DISTINCT t.term_id) AS total, COUNT(t.term_id) AS c_total
FROM wp_terms AS t
INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id AND tt.taxonomy = 'asin'
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'

備忘のため解説。

  1. ターム(本)の数を数える。一冊の本が複数の投稿に属していることがあるので、本の数 (total) と延べ引用数 (c_total すなわち cumulative total) を取得する。
  2. タームが保存されているのは wp_terms テーブル。
  3. asin タクソノミーに属するタームだけを抽出する。
  4. それらのタームが関連付けられている投稿を探し出すために、wp_term_relationships テーブルを経由する。
  5. タームが関連付けられている投稿のうち、投稿ステータスが公開かつ投稿タイプが投稿のものだけを抽出する。
  6. それらの投稿のカテゴリを探し出すために、wp_term_relationships テーブルを経由する。
  7. それらの投稿に関連付けられたタームのうち category タクソノミー下にあるものだけを抽出する。
  8. その category タクソノミー下のタームが list であるものだけを抽出する。

下は、このSQL文を組み立てるときに頭の整理に使った表。個人的にはわかりやすく作れたのでこれも備忘のためにメモ。# 列の数字が上のSQL文の行番号に相当する。新しいテーブルを ON(太字のセル=その直上のセル) で JOIN したうえで、太字セルの右セルのAND句で絞り込んで……を重ねていくイメージ。

#ASJOINANDJOINJOINANDJOINJOINANDJOINAND
wp_termstterm_id
3wp_term_taxonomyttterm_idtaxonomy = asinterm_taxonomy_id
4wp_term_relationshipsrterm_taxonomy_idobject_id
5wp_postspIDpost_status = publish
AND
post_type = post
ID
6wp_term_relationshipsr2object_idterm_taxonomy_id
7wp_term_taxonomytt2term_taxonomy_idtaxonomy = categoryterm_id
8wp_termst2term_idname = list

ちなみに、絞り込み部分をすべて WHERE句にまとめても同じ結果が得られるがずいぶんわかりづらい。

SELECT COUNT(DISTINCT t.term_id) AS total, COUNT(t.term_id) AS c_total
FROM wp_terms AS t
INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id
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
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
INNER JOIN wp_terms AS t2 ON tt2.term_id = t2.term_id
WHERE tt.taxonomy = 'asin' AND p.post_status = 'publish' AND p.post_type = 'post' AND tt2.taxonomy = 'category' AND t2.name = 'list'

Posted by

on

in category