■概要
ひょんな事から、sqliteでの全文検索について調べました。すると全文検索用に、fts3/4指定ができる事を発見したのですが、予想通り"スペース"での、単語区切りしかできないようです。
日本語を処理したかったので、自分でn-gramしたデータを登録してテストしてみました。
確かに高速に検索できます。
※ MeCabを利用したtokenizerとかはandroidでの利用を考え考察していません。
■テストデータ
郵便番号データを下記からダウンロードしました。http://www.post.japanpost.jp/zipcode/dl/kogaki.html
全国版データを利用しました。
ちなみにcentos6.xではlhaコマンドを入手できなかったので、http://d.hatena.ne.jp/pcmaster/20100211/p3 を参考にインストールしました。
$ lha e ken_all.lzh
$ ruby gensql.rb ken_all.csv > ken_all.sql # 下記スクリプトで分割済みのフィールドを準備
■実験
$ sqlite3 a.sqlite> create virtual table zip_codes using fts3
(
code text
, address text
, search_field text
);
> begin;
> .read ken_all.sql
> commit;
> .timer ON;
> select code, address from zip_codes where address like ('%東成区%');
CPU Time: user 0.272959 sys 0.024997
> select count(*) from zip_codes where address like ('%東成区%');
CPU Time: user 0.262960 sys 0.032995
> select code, address from zip_codes where search_field match '東成 成区';
CPU Time: user 0.001000 sys 0.000000
> select count(*) from zip_codes where search_field match '東成 成区';
CPU Time: user 0.001000 sys 0.000000
なんと数値上は300倍近く高速化されましたw!
ちなみに検索文字列もn-gram分割する必要があります。
■(参考)データ加工用スクリプト
require 'csv' # なんちゃって2-gram。英数字は特別扱いするとか正規化するとか工夫してください。 # javaで組めば幸せになるはず(?) def gen_2_gram(org) result = [] (org.length - 1).times { |i| result << org[i, 2] } result.join(" ") end # *.csvファイルを処理する。 ARGV.each do |file| CSV.foreach(file, "r:sjis") do |row| zip = row[0] address = (row[6] + row[7] + row[8]).encode("utf-8") puts "insert into zip_codes (code, address, search_field) values (#{zip}, '#{address}', '#{gen_2_gram(address)}');" end end
※"以下に掲載がない場合"とかは実験目的なので特に処理していません。