JavaScriptによる全文検索エンジン

JavaScriptでインデックス型の全文検索エンジンを作ってみた。全文検索エンジンを作る際に問題となるのは、インデックスデータを部分的に読み込む方法である。通常はmmapやpreadなどを使ってファイルの一部を部分的に読み込むのだが、もちろん、ブラウザには使えない。ブラウザでファイルの一部分を読み込むには2通りの方法がある。1つは、ファイルを多数のファイルに分割する方法であり、もう1つはHTTPリクエストのRangeヘッダを利用して、ファイルの一部を取得する方法である。前者の利点は、ブラウザのキャッシュが効くことや、対応ブラウザが多いことである。後者の利点は、ファイル数が少なくなるので、インデックスの管理が容易になることである。今回はRangeヘッダの実用性にも興味があったので、後者の方法を用いた。

参考ページ:最速インターフェース研究会 :: Ajaxを使ったシンプルなチャット

転置インデックスの構成は単純な 1gram + 連接情報にした。1gramだと検索速度が遅いが、インデックスサイズが大きいので今回は妥協した。2gramに対応するには、データ構造の洗練が必要である。インデックスサイズがまだまだ大きすぎる。なお、インデックス生成プログラムはRubyで書いた。最初はこちらもJavaScriptにしようと思ったのだが、色々と問題があって断念した。

苦労したのはJavaScriptではバイナリデータを扱えない点。その為、インデックス中の数値は全て文字列に変換する必要がある。例えば、数値の0はスペース(\u0020)に変換した。また、文字列のn-byte目の文字にアクセスする手段がないのも不便であった。JavaScriptではn文字目にアクセスする手段しかない。そのため、例えば、文字列のn-byteからm-byteを抜き出す(遅い)コードは次のようになる。

String.prototype.substr2 = function(offset, length){
    var out = "";
    var idx = 0;
    for(var i = 0; i < this.length; i++){
	if(offset <= idx && idx < offset + length){
	    out += this.charAt(i);
	}
	var code = this.charCodeAt(i);
	if(code < 0x80){
	    idx += 1;
	}else if(code > 0x7FF){
	    idx += 3;
	}else{
	    idx += 2;
	}
    }
    return out;
}

ただ、全文検索エンジンの作成自体は結構楽しかった。多分、チューニング作業が好きなのだと思う。削れる余地はまだまだいくらでもあるので、しばらく遊べるかも。

デモとして、はてなブックマークの過去の人気エントリー25000件のタイトルの検索サービスを公開してみます。デモページはJavaScriptプログラムやインデックスデータなどの静的なファイルのみから構成されています。

デモページ:名古屋市で賢い借金返済方法を教えます!

AND検索も出来ないし、バグだらけですが、よかったら試してみてください。1gramなので、ひらがなやカタカナの検索は結構遅いかもしれません。