DBD::SQLite 1.30_04/05

遅ればせながらDBD::SQLiteの新しい開発版が出ました。今回の目玉はLaurent Dami氏が実装してくださったFTS3の拡張機能。これを使うとDBD::SQLiteでも簡単に日本語全文検索ができるようになります。

使い方はこんな感じ。

use strict;
use warnings;
use DBI;
use Text::MeCab;

my $mecab = Text::MeCab->new;

my $dbh = DBI->connect('dbi:SQLite::memory:');
$dbh->do('create virtual table foo using fts3 (content, tokenize=perl "main::tokenizer")');

my $sth = $dbh->prepare('insert into foo values (?)');
$sth->execute('吾輩は猫である。');
$sth->execute('名前はまだ無い。');

my $res = $dbh->selectall_arrayref('select docid, content from foo where content match ?', undef, '猫');

print "@$_\n" for (@$res);

sub tokenizer { return sub {
    my $node = $mecab->parse($_[0]);
    my $index = 0;
    my $pos = 0;
    return sub {
        my $token  = $node->surface or return;
        my $length = $node->length;
        my $start  = $pos;
        my $end    = $pos += $length;
        $node = $node->next;
        return ($token, $length, $start, $end, $index++);
    };
}}

詳しくはDBD::SQLiteのPODや、SQLite本家の解説ページをご覧いただくとして、ここではmain::tokenizerが返す無名関数のポインタをSQLiteに渡し、その無名関数を実行したときに返ってくる無名関数を個々のトークン取得の際に利用する、という仕組みになっています。

テーブル定義にないdocidはFTS3を利用すると暗黙で用意されるrowidカラムの別名。

実際にどこがマッチしたのかを知りたい場合は、selectall_arrayrefの行をこのように変えるとヒットした単語の前後にタグを入れられます。

my $res = $dbh->selectall_arrayref('select docid, snippet(foo, "<b>", "</b>") from foo where content match ?', undef, '猫');
1 吾輩は<b>猫</b>である。

snippetの第一引数のfooは暗黙のうちに用意されているテーブル名と同じ名前のカラムとのこと。このあたりは暗黙の了解が多いので、おかしいなと思ったら本家サイトの解説を読んで、カラム定義などをあわせておくのが無難なようです。

なお、この版からコンパイル時にSQLITE_ENABLE_FTS3_PARENTHESISがつくようになりました。拡張構文が有効になるためSQLの内容によっては解釈がかわる場合があります。国内ではまずいないと思いますが、万一すでにDBD::SQLiteでFTS3を利用している方がいらっしゃいましたら、動作確認をお願いします。

また、この版からはSQLite 3.7系列がバンドルされています。Write-Ahead Loggingという新しい機能が利用できるようになっていますので、興味のある方はお試しあれ。

この1.30_04/05は次のrelease candidateです。二週間内外で問題が出てこなければ1.31としてリリースされる予定なので、テストやアプリケーションの一部にDBD::SQLiteを利用している方はあらかじめ最新の開発版で問題がないかテストをお願いします。