2010/11/19

Segmenterで分かち書きしたテキストをSQLiteで全文検索

NILScriptには軽量データベースエンジンSQLiteを扱うクラスが用意されています。
SQLiteには文字列中の単語をインデックス化して高速な全文検索を行うFTS3という機能が用意されていますが、標準搭載されている単語分割処理は文字列を空白文字で分割するだけの簡易的なものなので、そのままでは日本語のテキストをまともに処理できません。

そこで、日本語の文字列を分かち書きする機能を提供する「Segmenter」クラスを新たに用意しました。
アルゴリズム本体はプラグイン方式で追加できるようになっており、現在はTinySegmenter- Javascriptだけで実装されたコンパクトな分かち書きソフトウェア高速化のための修正を加えたものにSpiderMonkey独自機能を利用したチューニングを加えて作成したプラグインが用意されています。
TinySegmenterは簡易的な物なので、「すもももももももものうち」のように平仮名が連続していたりすると「すも | も | も | もも | も | ももの | うち」のようなおかしな結果になってしまうことがありますが、検索に利用するだけなら十分でしょう。(検索語句の「すもも」も「すも | も」として検索されるため)
より正確な分かち書きが必要な場合は、MeCabなどのエンジンを利用したプラグインを作成するとよいでしょう。

以下のスクリプトは、SegmenterとSQLiteを組み合わせて全文検索を行うサンプルです。
var segmenter=require('Segmenter').Segmenter.create("ja");
var db=require('SQLite').DB.open();
db.execute("CREATE VIRTUAL TABLE docs USING fts3(title TEXT, body TEXT)");
db.begin();
Main.directory.directory('doc').files.execute(function(f){
    db.table('docs').insert([{title:f.baseName,body:segmenter.segment(f.load()).join(' ')}]);
});
db.end();

var q;
while(q=prompt("search query","")){
    println("search result for: "+q);
    db.select([,'docs','body MATCH $q'],"title",-1,0,{q:'"'+segmenter.segment(q).join(' ')+'"'}).execute(function(o){
        println(o.title);
    });
}
free(db,segmenter);
各クラスの基本的な使い方については、同梱のドキュメントなどを参照してください。

全文検索型のテーブルを作成するには、「"CREATE VIRTUAL TABLE name USING fts3(colnames)」のようにします。
そして、挿入する値を指定する際に、分かち書きした値を指定します。Segmenterオブジェクトのsegment()メソッドは配列を返すので、空白で連結する必要があります。

検索時にMATCH演算子を使うことで、インデックスを利用した全文検索を行なえます。
検索語句は挿入時と同様にSegmenterで分割して空白区切りにし、前後に「"」を付加する必要があります。
MATCH演算子の詳しい使い方はSQLite FTS3 Extensionなどを参照してください。

上記のスクリプトを実行すると、キーワード入力用のプロンプトが表示され、入力した語句を含むドキュメントが列挙されます。
プロンプト表示までに発生する2秒ほどの待ち時間は、主にSegmenterの処理による物のようです。
個人利用で扱う程度のテキスト量なら、このくらいの処理速度で十分実用に耐えられるでしょう。

0 件のコメント:

コメントを投稿