PostgreSQL8.3(内蔵tsearch2)を使ってファイルサーバの全文検索システムを構築する(2.アプリケーション編)

ファイルサーバをPostgreSQLで全文検索〜2.アプリケーション編

前提

環境構築編の内容によって,以下の環境が整ったとする.

  • PostgreSQL 8.3 beta3
  • mecab 0.96
  • wakachi (SRA OSSが公開しているもの)
  • DBI,DBD::Pg

この状態で,いよいよ以下から検索システムをダウンロードする.

  • http://kamoland.com/comp/fss.zip
    • 2008/2/5: fss_da.plを変更.検索語が複数語で構成される場合に検索漏れが生じないように,WHERE句を修正した
    • 2007/12/9: 初版

このzipを展開すると,以下のファイルが入っている.

ファイル名説明
create.sqlテーブル定義などのDDL
indexer.plファイルサーバの内容をDBに登録するためのインデックサ
zindex.cgi検索に使用するWebインタフェイス
fss_const.cgi定数設定ファイル
fss_da.plデータベースアクセス処理を集めたファイル
fss_ca.plファイルコンテンツアクセス処理を集めたファイル
fss_util.plその他の共通処理を集めたファイル

以下では,この一式をfssと呼ぶことにする.(File Server Search)


1.データベースの準備

fss_testという名前のデータベースを作ることにする.
createdb fss_test
createlang -d fss_test plpgsql
psql -d fss_test -f /usr/local/pgsql/share/contrib/wakachi.sql
この状態で,fssに含まれるDDLを流す.
 psql -d fss_test -f create.sql

これで,必要なテーブルとファンクション,トリガーが作成される.

そして,fss_const.cgiに記述しているデータベースへの接続設定を, 各自の環境に合わせて編集する.

fss_const.cgi より

$cns::db_host = "inspiron";
$cns::db_dbname = "fss_test";
$cns::db_dbuser = "postgres";
$cns::db_passwd = "";
上記のようにDBのホスト名,データベース名,接続ユーザ名,パスワードの設定があるので,これを各自の環境に合わせて記述する.


2.プログラムファイルの配置

fss構成ファイルのうち,拡張子がpl,cgiのファイルを1つのディレクトリ内に配置する. ブラウザから検索を実行するためには,cgiの実行が可能なディレクトリに置く必要がある.

また,以下のファイルについては,chmod等で実行属性を立てておく必要がある.

  • indexer.pl
  • zindex.cgi


3.インデックサの起動

indexer.plを実行して,インデックス化を開始する.

引数として,インデックス化対象ディレクトリを渡す. また,-xオプションで除外ディレクトリを渡すことも可能.いくつか例を示す.

/var/home/doc/ 配下のファイルをインデックス化する場合
./indexer.pl /var/home/doc/

/var/home/doc/ 配下のファイルのうち /var/home/doc/skip/ 配下以外をインデックス化する場合
./indexer.pl /var/home/doc/ -x /var/home/doc/skip/

ディレクトリは以下のように複数指定も可能
./indexer.pl /var/home/doc/ -x /var/home/doc/skip/ /var/home/doc2/ -x /var/home/doc2/skip1/ -x /var/home/doc2/skip2/

実行すると,何やらログがたくさん表示されて,ファイル内容のDBへの登録処理が行われる. このバージョンではテキストファイルだけが対象なので,それ以外のファイルはスキップされます. 対応ファイルの追加については後述する.

また,ファイルサイズの制限も設定しており,1MB以上のファイルはスキップされます. 設定箇所は以下なので適当に調整してください.

indexer.pl の17行目

my $FILESIZE_LIMIT = 1024*1024; # 1MB

このインデックス化の処理は1件毎にDBにコミットしているため, 処理途中でインデックサをCTRL+C等で中断しても,それまでに行った処理はちゃんとDBに反映されています.


4.検索の実行

ブラウザから zindex.cgi を呼び出せば,以下のように検索条件入力画面が表示される.

zenbun2a.png

検索キーワードを入力して検索ボタンを押下すると,以下のように検索結果が表示される.

zenbun2b.png

めでたし.これでひとまず基本的な検索が行えたので,ここから色々改造することにする.


5.インデックス化可能なファイルの種類の追加

先ほどのバージョンでは,テキストファイルしかDBに登録して検索することができない.

これは物足りない.

そこで対応できるファイルの種類を増やすのだが,それにはfss_ca.plを改造すればよい.

必要な対応は以下になる.

  1. ファイルタイプの定数を追加する.ファイルタイプの定数とは,$ca::FT_PLAINなどのこと
  2. 拡張子とファイルタイプの関連づけを登録する.具体的には,%filetypeMapのこと
  3. ファイルタイプとファイルの形式変換関数の関連づけを追加する.具体的には,%handlerMapのこと
  4. ファイル形式変換関数を作成する.この関数は,対象ファイル名を引数として,その内容をテキスト化した文字列を返す必要がある

(1) PDFファイルへの対応を追加する場合

実際にPDFファイルへの対応を追加する場合は,以下の対応でOKだ.

1. ファイルタイプ定数を追加する.

以下の行を16行目あたりに挿入する.
$ca::FT_PDF = 3;

2. 拡張子との関連づけ

%filetypeMapの定義内容を,以下のように変更する.

[変更前]
	'pdf' => $ca::FT_NOT_SUPPORT,

[変更後]
	'pdf' => $ca::FT_PDF,

3. ファイル形式変換関数との関連づけ

%handlerMapの定義箇所に,以下の内容を追加する. ここでは変換関数の関数名を,pdfHandlerとしている.
	$ca::FT_PDF => \&pdfHandler,

4. ファイル形式変換関数を作成する

3.で%handlerMapに追加した名前の関数を作成して,末尾にでも追加する.
# ファイルの形式変換(PDF)
#
# xpdf
# http://www.foolabs.com/xpdf/
# のpdftotextコマンドを使用する
# Japaneseパッケージも必要
#
sub pdfHandler {
	my ($filename) = @_;

	my $contents = readContents($filename);

	# 一旦一時ファイルに保存してから,pdftotextをかける
	my $tempPdfFile = "/tmp/indexerPdf.$$";
	my $tempTextFile = "/tmp/indexerText.$$";

	open(FH, '>:bytes', "$tempPdfFile") || die "ERROR: Can't open file=$tempPdfFile";
	print FH $contents;
	close(FH);

	system("pdftotext -enc Shift-JIS $tempPdfFile $tempTextFile");
	unlink($tempPdfFile);

	$contents = '';
	if (open(FH, '<:bytes', "$tempTextFile")) {
		while (<FH>) {
			$contents .= Encode::decode('shiftjis', $_);
		}
		close(FH);
		unlink($tempTextFile);

	} else {
		print "ERROR: Can't open file=$tempTextFile\t";
		return '';
	}

	return $contents;
}
ファイル形式変換関数の共通仕様として,
  • 引数;対象ファイル名のフルパス
  • 戻り値:その内容をテキスト化した文字列

になっている.

なお,コメントにも書いてあるとおり,この関数では外部コマンドとしてxpdfのpdftotextコマンドを呼び出しているので, 一応以下に,xpdfのセットアップについて記述する.

xpdfのセットアップ

http://www.foolabs.com/xpdf/ から,
  • xpdf-3.02pl2-linux.tar.gz
  • xpdf-japanese.tar.gz

をダウンロードする.今回は面倒なのでソースではなくLinux用バイナリを使用することにした.

あとは以下のような感じだ.

tar xzf xpdf-3.02pl2-linux.tar.gz
tar xzf xpdf-japanese.tar.gz
su
cp xpdf-3.02pl2-linux/pdftotext /usr/local/bin/
mkdir -p /usr/local/share/xpdf/japanese/
cp -R xpdf-japanese/* /usr/local/share/xpdf/japanese/
cat xpdf-japanese/add-to-xpdfrc >> /usr/local/etc/xpdfrc

(2) Excelファイルへの対応を追加する場合

次に同じように,Excelファイルへの対応例.

1. ファイルタイプ定数を追加する.

$ca::FT_EXCEL = 4;

2. 拡張子との関連づけ

[変更前]
	'xls' => $ca::FT_NOT_SUPPORT,

[変更後]
	'xls' => $ca::FT_EXCEL,

3. ファイル形式変換関数との関連づけ

	$ca::FT_EXCEL => \&excelHandler,

4. ファイル形式変換関数を作成する

# ファイルの形式変換(Excel)
#
# xlhtml
# http://chicago.sourceforge.net/xlhtml/
# のxlhtmlコマンドを使用する
#
sub excelHandler {
	my ($filename) = @_;

	my $contents = readContents($filename);

	# 一旦一時ファイルに保存してから,xlhtmlをかける
	my $tempInFile = "/tmp/indexerXls.$$";
	my $tempTextFile = "/tmp/indexerText.$$";

	open(FH, '>:bytes', "$tempInFile") || die "ERROR: Can't open file=$tempInFile";
	print FH $contents;
	close(FH);

	system("xlhtml $tempInFile > $tempTextFile");
	unlink($tempInFile);

	$contents = '';
	if (open(FH, '<:bytes', "$tempTextFile")) {
		while (<FH>) {
			$contents .= $_;
		}
		close(FH);
		unlink($tempTextFile);

		$contents = htmlFilter($contents);

	} else {
		print "ERROR: Can't open file=$tempTextFile\t";
		return '';
	}

	return $contents;
}
Excelの場合は,xlhtmlを外部コマンドとして呼び出してテキストに変換している.

xlhtmlのセットアップ

http://chicago.sourceforge.net/xlhtml/ から,
  • xlhtml-0.5.gz

をダウンロードする.

コンパイル時に「depcomp」が必要だといわれるので,どこかにないか探しておく.

# locate depcomp
/usr/share/automake-1.6/depcomp
/usr/share/automake-1.9/depcomp
/usr/share/automake-1.5/depcomp
/usr/share/automake-1.7/depcomp
上記のように4個も見つかってしまった.良くわからないが一番新しいのを使うことにする.

tar xzf xlhtml-0.5.gz
cd xlhtml-0.5
./configure
cp /usr/share/automake-1.9/depcomp .
make
su
make install

PDFとExcelも扱えれば,まずまずだろう.

kamolandをフォローしましょう


© 2021 KMIソフトウェア