高速なKVSであるTokyo Cabinetと高速なWebサーバStarmanを試してみたいなぁ、と思い、Ajaxを使った簡単な住所検索アプリを作ってみました。
※wataさんが、Tokyo Cabinetの後継のKyoto Cabinetを「Kyoto Cabinet + Starmanで住所検索 ...を試してみた - ワタブログ」で紹介されています。
PlackとStarmanをインストール
CPANでPlackとStarmanをインストールします。
% sudo cpan Plack
% sudo cpan Starman
Tokyo CabinetとPerl用APIをインストール
ソースパッケージとPerl用APIを「データベースマネージャ Tokyo Cabinet」からダウンロードします。
tokyocabinet-1.4.45.tar.gz
perlpkg/tokyocabinet-perl-1.33.tar.gz
まずはTokyo Cabinetのインストール。私の環境(Ubuntu 9.10)ではbzlib.hが不足してたので、先にlibbz2-devをインストールしておきました。
% tar xzvf tokyocabinet-1.4.45.tar.gz
% cd tokyocabinet-1.4.45
% ./configure --prefix=/usr/local/tokyocabinet
% make
% sudo make install
次にPerl用APIをインストールします。
% tar xzvf tokyocabinet-perl-1.33.tar.gz
% cd tokyocabinet-perl-1.33
% PATH=$PATH:/usr/local/tokyocabinet/bin perl Makefile.PL
% make
% make test
% sudo make install
KEN_ALL.tchを作成
住所検索の情報となる郵便番号データを日本郵便のサイト「郵便番号データダウンロード - 日本郵便」からダウンロードします。“読み仮名データの促音・拗音を小書きで表記しないもの”の“全国一括”のken_all.lzhです。
これを展開したKEN_ALL.CSVから、以下のスクリプトを使ってTokyo Cabinetのハッシュデーベースファイルを作ります。今回作る住所検索アプリでは郵便番号を1桁入力した時点から候補を表示するようにしたいので、1 桁~7桁のパターン毎に10個までの候補を記録しておきます。テーブルデータベースを使うともっとスマートに構成できそう?
また、Perl用APIはOOで扱う事ができますが、今回は手軽にtieを使いました。
#!/usr/bin/perl
use 5.008001;
use utf8;
use strict;
use warnings;
open my $ken_all_csv, '<:encoding(shiftjis)', 'KEN_ALL.CSV'
or die 'failed to open KEN_ALL.csv';
my %ken_all_tmp;
{
local $/ = "\x0D\x0A";
while ( my $record = <$ken_all_csv> ) {
my ($zip_code, @address) = ( split /,/, $record )[2, 6, 7, 8];
for my $value ($zip_code, @address) {
$value =~ s/\A "(.*)" \z/$1/xms;
}
if ( $address[2] eq '以下に掲載がない場合'
or $address[2] =~ / の次に番地がくる場合 \z/xms
) {
$address[2] = q{};
}
else {
$address[2] =~ s/ 一円 \z//xms;
}
my $address = join q{}, @address;
DIGIT:
for my $digit ( 1 .. 7 ) {
my $zip_code = substr $zip_code, 0, $digit;
next DIGIT if @{ $ken_all_tmp{$zip_code} ||= [] } >= 10;
push @{ $ken_all_tmp{$zip_code} }, $address;
}
}
}
use TokyoCabinet;
use constant OWRITER => TokyoCabinet::HDB::OWRITER;
use constant OCREAT => TokyoCabinet::HDB::OCREAT;
tie(my %ken_all, 'TokyoCabinet::HDB', 'KEN_ALL.tch', OWRITER | OCREAT)
or die 'tie error';
while ( my ($zip_code, $addresses) = each %ken_all_tmp ) {
$ken_all{$zip_code} = join q{<br />}, @$addresses;
}
PSGIを作成
住所検索アプリとなるプログラムをPSGI仕様で作成します。こちらもtieを使いました。
Ajaxリクエストのトリガーとする郵便番号の入力捕捉は、keydownイベントをそのまま使い、キーコードも使わずsetTimeoutでタイミングをずらして値をそのまま取得しています。
use strict;
use warnings;
use TokyoCabinet;
use constant OREADER => TokyoCabinet::HDB::OREADER;
my $html = do { local $/; <DATA> };
my $app = sub {
my ($env) = @_;
return [ 200, [ 'Content-Type' => 'text/html' ], [ $html ] ]
if $env->{REQUEST_URI} !~ m{\A /search\?zip_code=(.*) }xms;
my $zip_code = $1;
tie(my %ken_all, 'TokyoCabinet::HDB', 'KEN_ALL.tch', OREADER)
or die 'tie error';
my $address = $ken_all{$zip_code} || q{};
return [ 200, [ 'Content-Type' => 'text/html' ], [ $address ] ];
};
__DATA__
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>ken_all</title>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript"><!--
google.load('jquery', '1.4'); // -->
</script>
<script type="text/javascript"><!--
jQuery( function ($) {
$('input#zip_code').keydown( function () {
var $this = $(this);
setTimeout( function () {
$.get( '/search', { zip_code: $this.val() }, function (data) {
$('div#address').html(data);
} );
}, 0 );
} );
$('input#zip_code').focus();
} ); // -->
</script>
</head>
<body>
<form action="/search" method="get">
<div><input id="zip_code" type="text" /></div>
<div id="address"></div>
</form>
</body>
</html>
PSGIを起動してブラウザでアクセス
作成したPSGIをStarmanで起動します。デフォルトのポート5000でWebサーバとして動作します。これで準備完了です。
% starman ken_all.psgi
ブラウザからWebサーバにアクセスします。ローカルホストであればURLはhttp://localhost:5000/です。テキスト入力欄に数値を入力すると、その下に瞬時に住所が表示されます。速い!
コメント