MapReduce::Liteを使って、転置インデックスを作成するプログラムを書きました。
docIDとかwordIDとかは無しで、ファイル名や単語をそのままキーとして使うようにしています。
全体的な流れとしては、
- 複数のファイルを読み込む(perl reverse_index.pl index.html top.html)
- mapで(ファイル名、ファイルの中身)を受け取り、(単語、ファイル名:単語の位置)を生成する
- reduceで(単語、{ファイル名:単語の位置}リスト)を生成する
という感じになっています。
プログラムは以下のような感じです。
package ReverseIndex::Mapper; use Moose; with 'MapReduce::Lite::Mapper'; use Text::MeCab; has word_id => ( is => 'rw', isa => 'HashRef', default => sub { {} } ); has doc_pos => ( is => 'rw', isa => 'Int', default => sub { 0 } ); sub map { my ($self, $key, $value) = @_; my $mecab = Text::MeCab->new; for (my $node = $mecab->parse($value); $node; $node = $node->next) { if ($node->posid =~ m/^(?:1|2|3|4)$/) { my $word = $node->surface; unless ($self->word_id->{$word}) { $self->emit( $word => $key.":".$self->doc_pos ); $self->word_id->{$word} = 1; $self->{doc_pos} += 1; } } } } package ReverseIndex::Reducer; use Moose; with 'MapReduce::Lite::Reducer'; sub reduce { my ($self, $key, $values) = @_; $self->emit($key => $values->join(',')); } package main; use MapReduce::Lite; use FindBin::libs; my $spec = MapReduce::Lite::Spec->new(intermidate_dir => "./tmp"); for my $arg (@ARGV) { my $in = $spec->create_input; $in->file($arg); $in->mapper('ReverseIndex::Mapper'); } $spec->out->reducer('ReverseIndex::Reducer'); $spec->out->num_tasks(3); mapreduce($spec);
このプログラムに以下のようなテキストファイルを読み込ませます。
index.html
This is a pen
これは可愛い犬ですね。
これはペンです。
わんわん、犬だよ
top.html
That is a book
これは可愛い犬ですね。
綺麗な星だね。
「perl reverse_index.pl index.html top.html」と実行した結果が下記のようになりました。
pen => index.html:3
これ => index.html:4,top.html:4
ペン => index.html:7
可愛い => index.html:5,top.html:5
星 => top.html:8
犬 => index.html:6,top.html:6
綺麗 => top.html:7
That => top.html:0
This => index.html:0
a => index.html:2,top.html:2
book => top.html:3
is => index.html:1,top.html:1