タグによる記事の紐付けは、日々様々な興味に浮気しがちに綴られるブログには最適の分類手法なのかも知れません。このサイトでもタグによる分類を導入していますが、カテゴリが異っても同じ話題に触れた記事を横断的に閲覧できる仕組みとして大変重宝しています。それでいて、従来のカテゴリによる階層的分類にも少なからず利点を感じる場合もあって、タグによる分類一本で万事事足りるということはないようです。特にテーマがハッキリとした記事の巡回には、カテゴリによる階層的分類の方が十分効果的な場合があります。
ユーザに多面的な記事の側面を提供すると云う点からも、これら両者の分類手法を共存させ、互いに補う関係であるところを是非実現したいと考えます。そのために、本エントリでは現在は独立したものとして存在しているタグとカテゴリを橋渡すための方法を紹介しています。
タグによる分類機能の実現にはOgawa::Memorandaで公開されているTagwire Pluginを使用することができます。しかし、Tagwire Plugin の <MTTags> は常にブログで使用されている全てのタグを列挙するので、特定のアーカイブで使用されているタグだけを抽出したりすることができません。
そこで今回、現在のアーカイブコンテクストで使用されているタグのみを列挙するテンプレートタグを追加しました。追加されたテンプレートタグ <MTTagsInEntries> は、現在のコンテクストで有効なエントリに含まれるタグのみを列挙します。つまり、月別アーカイブで使用した場合には、該当月のエントリに含まれているタグのみが列挙されます。同様にカテゴリアーカイブで使用した場合には、そのカテゴリ内のエントリに含まれているタグのみが列挙されます。
この機能を用いてタグを列挙することによって、例えば、月ごとに記事投稿者の興味があったタグのランキングを作成したり、またカテゴリ毎にそのカテゴリを代表するタグをリストすることができます。例として、このサイトの PHOTO カテゴリには
"観光"や"旅行"というタグが並び、ソフトウェア カテゴリでは
"C言語"や"JavaScript"、"SDK"と云ったタグが並んでいるのを見ることができます。これはそのカテゴリ内の記事で使用されているタグを安直に使用頻度で列挙しただけなのですが、このようにカテゴリ内で頻出するタグのリストは、概ねそのカテゴリに含まれる記事内容を代表しています。そして結果的には、タグとカテゴリという異なった切り口を持った分類手法をバイパスすることになります。
また、カテゴリ毎に代表タグが抽出できることは、違った見方をすれば、タグそのものをカテゴライズしているとも言えます。分類のための"タグ"を"カテゴリ"に分類しているのです。
Tagwire Plugin の改造箇所を以下に示します。tagwire.pl をエディタで開き、以下のコードを追加します。
;# MT::Template::Context->add_container_tag (... が続くあたりに追加します
;# piroli++, '05/09/26
MT::Template::Context->add_container_tag('TagsInEntries' => &tags_in_entries);
;# ++piroli, '05/09/26
;# sub tags {...} の後ろにでも追加します
;# piroli++, '05/09/26, based on tags ()
sub tags_in_entries {
my ($ctx, $args, $cond) = @_;
# sort_by (tag/tag-case/count, default = tag)
my $sort_by = $args->{sort_by} || 'tag';
# sort_order (ascend/descend, default = ascend)
my $sort_order = $args->{sort_order} || 'ascend';
# lastn (default = 0, no cutoff)
my $lastn = $args->{lastn} || 0;
# case_sensitive (0/1, default = 1)
my $case_sensitive = defined $args->{case_sensitive} ? $args->{case_sensitive} : 1;
my $blog_id = $ctx->stash('blog_id');
my %tags = ();
my %ts = ();
my $data = get_pd_indexes($blog_id) || get_db_indexes($blog_id)
or return '';
;### カテゴリに属するエントリに限ってタグを抽出する
my $entries = $ctx->stash('entries')
or return $ctx->_no_entry_error('MTTagsInEntries');
my %tindex = %{$data->{tindex}};
my %eindex = %{$data->{eindex}};
foreach my $e (@$entries) {
foreach (@{$eindex{$e->id}->{tags}}) {
my $t = $case_sensitive ? $_ : lc $_;
$tags{$t}++;
$ts{$t} = $tindex{$_}->{ts} if (! exists $ts{$t} || $ts{$t} < $tindex{$_}->{ts});
}
}
my @list;
if ($sort_by eq 'tag' || $sort_by eq 'keyword' ) {
@list = $sort_order eq 'ascend' ?
sort { lc $a cmp lc $b } keys %tags :
sort { lc $b cmp lc $a } keys %tags;
} elsif ($sort_by eq 'tag-case' || $sort_by eq 'keyword-case') {
@list = $sort_order eq 'ascend' ?
sort keys %tags :
sort reverse keys %tags;
} elsif ($sort_by eq 'date') {
@list = $sort_order eq 'ascend' ?
sort { $ts{$a} <=> $ts{$b} } keys %tags :
sort { $ts{$b} <=> $ts{$a} } keys %tags;
} else {
@list = $sort_order eq 'ascend' ?
sort { $tags{$a} <=> $tags{$b} } keys %tags :
sort { $tags{$b} <=> $tags{$a} } keys %tags;
}
$ctx->stash('Tagwire::tags_total', scalar @list);
my $total_sum = 0;
$total_sum += $tags{$_} foreach (@list);
$ctx->stash('Tagwire::tags_total_sum', $total_sum);
my @res;
my $builder = $ctx->stash('builder');
my $tokens = $ctx->stash('tokens');
my $i = 0;
foreach (@list) {
last if $lastn && $i >= $lastn;
local $ctx->{__stash}{'Tagwire::tag'} = $_;
local $ctx->{__stash}{'Tagwire::tag_count'} = $tags{$_};
local $ctx->{__stash}{'Tagwire::tag_date'} = $ts{$_};
defined (my $out = $builder->build($ctx, $tokens))
or return $ctx->error($ctx->errstr);
push @res, $out;
$i++;
}
my $glue = $args->{glue} || '';
join $glue, @res;
}
;# ++piroli, '05/09/26
オリジナルコードの著作権はogawaさん
(Ogawa::Memoranda)にあります。
また上記の改造コードの扱いについては Tagwire Plugin のそれに準じます。
http://www.perl.com/language/misc/Artistic.html
あるカテゴリに属するエントリで使用されているタグのみを使用する場合、例えば以下のようにアーカイブテンプレートを修正します。なお、使用できるタグやオプションは <MTTags> と全く同様です。
<h4>このカテゴリの代表タグ</h4> <ul> <MTTagsInEntries sort_by="count" sort_order="descend" lastn="5"> <li><$MTTag$> [<$MTTagCount$>]</li> <ul> <MTRelatedTags sort_by="count" sort_order="descend" lastn="3"> <li><$MTTag$></li> </MTRelatedTags> </ul> </MTTagsInEntries> </ul>