ダウンロード:
fooling-dist-20080220.tar.gz
(415KBytes, 要 Python2.4以上)
[SVN]
[更新履歴]
デモ: http://fooling.tabesugi.net:8080/
ご意見・ご質問はこちらへ: http://groups.google.com/group/fooling-users
Fooling (風鈴) は Python用の文字列検索フレームワークです。
Fooling (風鈴) は Python と ありあわせのライブラリでつくった 文字列検索のためのフレームワークです。数千〜数十万件の文書の中から 日本語の文字列を検索・表示するのに向いています。 Fooling はおもに電子メール、掲示板への投稿や日記など、 時間とともに蓄積し、変更が比較的少ない文書を対象としています。
Fooling は単独で動かすソフトウェアというよりも、 Python プログラムに簡単に組み込んで使えるちょっとした検索機能を 提供することを目標としています。そのため、これは Python 本体と標準ライブラリがあれば動きます。 簡単なインターフェイスや該当文書の表示ルーチンは最初から提供されています。
Fooling では一般に以下のような検索条件が指定できます。 複数の条件はスペースで区切って組み合わせると and の意味になります。
フレーズ
あるいは "フレーズ"
title:フレーズ
-フレーズ
|フレーズ|
|日本|
は、「ここは日本です」にはマッチしますが、
「ここは日本海です」にはマッチしません。
date:年/月/日
あるいは date:年/月
あるいは date:年
-
) で範囲を指定することもできます。
例: date:2006/8/31
、date:2003/1-2005/3/8
、date:2004-2007
このほかにも、電子メールを検索する際には Subject:
や
To:
、Message-ID:
などのヘッダを個別に検索できます。
ここでは基本的な使い方のみを説明しています。各コマンドについての詳細は Fooling コマンド一覧 をごらんください。また、コマンドラインツールは Fooling の機能のすべてを サポートしているわけではありません。より高度な使い方をするには API を使ってプログラミングしてください。
特別な操作は必要ありません。
アーカイブを展開し、fooling/
以下のディレクトリを
Python のモジュールとして発見できるようにしておきます。
あるいは PYTHONPATH
を使ってもかまいません。
$ cp -Rp fooling/ /usr/local/lib/python2.4/site-packages/
2008年2月のバージョンから追加された読みがな検索を使うためには、
Fooling の Pythonコードが入っているのと同じディレクトリ
(通常は fooling-dist-2008xxxx/fooling
) に
yomidict.tcdb
というファイルが必要です。
このファイルを生成するには、fooling のトップディレクトリ
(fooling-dist-2008xxxx
) で以下の操作を実行してください:
$ make
Corpus
クラスを参照)
たとえば元の文書ファイル (HTML) が /doc/Python-2.4-ja/
以下に置かれているとします。
このディレクトリが文書ファイルの基底ディレクトリ (basedir) となります:
$ ls /doc/Python-2.4-ja/ about.html api doc icons inst mac ref whatsnew acks.html dist ext index.html lib modindex.html tut
次にインデックスファイル用のディレクトリ (ここでは /tmp/index/
とします) をつくり、
以下のように入力します:
$ indexer.py -b /doc/Python-2.4-ja/ -t HTMLDocument /tmp/index/ [インデックスするファイル名 ...]
indexer.py
コマンドは
与えられた文書から (ひとつ以上の) インデックスファイルを生成します。
オプション -b
は文書ファイルの基底ディレクトリ、
-t
は文書の種類 (HTML) を指定します。
実際には、コマンドライン引数から大量のファイルを直接指定しようとすると
「引数リストが長すぎる (Argument list too long)」といわれますので、以下のように引数のファイル名を省略し、
find
コマンドなどで標準入力からファイル名の一覧を与えてください。
$ ( cd /doc/Python-2.4-ja/; find . -type f -name '*.html' ) | indexer.py -b /doc/Python-2.4-ja/ -t HTMLDocument /tmp/index/ Index: basedir='/doc/Python-2.4-ja/', idxdir='/tmp/index/', max_docs_threshold=2000, max_terms_threshold=50000 ...(中略)...................Building index '/tmp/index/idx00000.cdb'(504)... ...(中略).............Building index '/tmp/index/idx00001.cdb'(419)... ...(中略).....................Building index '/tmp/index/idx00002.cdb'(513)... Done.
こうすると indexer.py
は
標準入力から与えられたファイル名を読み、そのファイルをインデックスに追加します。
このときのパス名は、基底ディレクトリからの相対パスで与えてやる必要があります。
以上でインデックスファイルができあがります。 Fooling では、大量の文書をいくつかの集合に分割して複数のインデックスファイルを作ります。 各インデックスファイルは独立しており、それぞれが最大 2000個 (デフォルト) の文書に 関する情報を格納しています。
$ ls -l /tmp/index/ -rw-r--r-- 1 yusuke users 5081895 Sep 3 13:18 idx00000.cdb -rw-r--r-- 1 yusuke users 4673407 Sep 3 13:19 idx00001.cdb -rw-r--r-- 1 yusuke users 4319317 Sep 3 13:20 idx00002.cdb
インデックスファイルは "xxxNNNNN.cdb
" のような名前になります。
最初の 3文字はプレフィックスで (デフォルトは idx
)、あとの 5文字は番号になっています。
ここでは idx00002.cdb
がもっとも新しい (最後に作られた) インデックスファイルです。
Fooling はこれらのインデックスファイルを辞書順の後ろから検索します。つまり、
最後にインデックスしたファイルが最初の検索結果として表示される
ようになっています。
ためしに検索してみましょう。
コマンドラインからの検索には selection.py
コマンドを使います。
検索する際には、文書の基底ディレクトリ (/doc/Python-2.4-ja/
) と、
インデックスファイルの置かれているディレクトリ (/tmp/index/
) を
両方指定する必要があります:
$ selection.py -b /doc/Python-2.4-ja/ -t HTMLDocument /tmp/index/ "python" "オブジェクト" 1: [Python チュートリアル] ... 7.2.1 ファイルオブジェクトのメソッド 7.2.2 pickle モジュール 8. エラーと例外... ... 10: [11. 標準ライブラリミニツアー - その 2] ... Python チュートリアル ... スレッド間の通信や調整にQueue オブジェクトを使うと、... 1-10 of about 388 results.
Fooling で使われるインデックスファイル (.cdb
ファイル) はそれぞれ完全に独立しています。
あるインデックスファイルが失われた場合、そこに登録されていた文書は検索できなくなりますが、
それ以外のインデックスファイルに含まれている文書は影響を受けません。
もちろん、失われたインデックスファイルは元の文書から再度作りなおすことが可能です。
インデックスに新しく文書を追加する場合は、
indexer.py
コマンドに追加するファイル名を指定します。
構文は同じです:
$ indexer.py -b /doc/Python-2.4-ja/ -t HTMLDocument /tmp/index/ newfile1.html newfile2.html Index: basedir='/doc/Python-2.4-ja/', idxdir='/tmp/index/', max_docs_threshold=2000, max_terms_threshold=50000 ..Building index '/tmp/index/idx00003.cdb'(2)... Done. $ ls -l /tmp/index/ -rw-r--r-- 1 yusuke users 5081895 Sep 3 13:18 idx00000.cdb -rw-r--r-- 1 yusuke users 4673407 Sep 3 13:19 idx00001.cdb -rw-r--r-- 1 yusuke users 4319317 Sep 3 13:20 idx00002.cdb -rw-r--r-- 1 yusuke users 40410 Sep 3 13:30 idx00003.cdb
indexer.py
コマンドは、すでに /tmp/index/
内に
インデックスファイルが存在している場合、最新のインデックスファイルの「次の名前」
(この場合は idx00003.cdb
) でインデックスファイルを作成します。
以後は idx00003.cdb
が最新のインデックスファイルとして利用されます。
Fooling では、新しく文書を追加するたびごとにインデックスファイルが
最低ひとつずつ増えていきますが、あまり少量の文書からなるインデックスファイルを
沢山つくると検索が遅くなるため、適当な間隔で
これらのファイルを統合する必要があります。
インデックスファイルの統合には
merger.py
コマンドを使います:
$ merger.py /tmp/index/ Remain: '/tmp/index/idx00000.cdb' Remain: '/tmp/index/idx00001.cdb' Merging: '/tmp/index/idx00002.cdb.new' (docs=515, est. terms=44382): [</tmp/index/idx00002.cdb (docs=513, terms=44382)>, </tmp/index/idx00003.cdb (docs=2, terms=872)>] ............................................done: docs=515, terms=44893, ents=582242 $ ls -l /tmp/index/ -rw-r--r-- 1 yusuke users 5081895 Sep 3 13:18 idx00000.cdb -rw-r--r-- 1 yusuke users 4673407 Sep 3 13:19 idx00001.cdb -rw-r--r-- 1 yusuke users 4344626 Sep 3 13:31 idx00002.cdb -rw-r--r-- 1 yusuke users 4319317 Sep 3 13:20 idx00002.cdb.bak -rw-r--r-- 1 yusuke users 40410 Sep 3 13:30 idx00003.cdb.bak
merger.py
コマンドは複数の小さいインデックスファイルに含まれる文書を
ひとつの大きなインデックスファイルにまとめます。上の例では
idx00003.cdb
に含まれていた 2つの文書が idx00002.cdb
に統合され、
古いファイルはそれぞれ idx00002.cdb.bak
と idx00003.cdb.bak
という名前で
残されています。古いインデックスファイルはすぐに rm
してかまいません。
デフォルトでは merger.py
は最大 2000件の文書までのインデックスファイルを作成します
(これ以上の文書は次のインデックスファイルに分割されます)。
インデックスファイルの統合はなるべくこまめに行ったほうがよいのですが、 この操作は 1ファイル中に含まれる文書数が多くなると時間がかかります (合計10000文書のインデックスを統合するには 10分ぐらいかかる)。 そのため、1日のうちに何十件も新しい文書が追加されるような場合は、 1時間に 1回程度の「細かい (100文書程度の)」統合をおこない、 「大きな (2000文書程度の)」統合は 1日に 1回だけおこなうようにすればよいでしょう。 なお、インデックスファイルの統合中にも検索はできますが、 インデックスファイルの統合と追加を同時に行わないでください。
ひとつのインデックスファイル中に含まれる最大文書数は -D
オプションで指定します:
これらは通常 cron タスクとして指定します。統合の回数を少なくするため、 できるだけ多くのファイルを一度にインデックスしたほうが効率的です。(1時間に 1回) $ merger.py -D 100 /tmp/index/ (1日に 1回) $ merger.py /tmp/index/
Fooling で使われているインデックスファイルの形式は、一度作成したら 書き換えることはできません。そのため、文書が変更された場合は基本的に 新規の文書としてインデックスに追加するか、あるいはすべての インデックスファイルをゼロから作り直すことになります。いくつかのケースでは、 これはインデックスをいくつかに分割することによって効率的におこなえます。
文書の更新頻度が少ない場合には、更新された文書を 新規文書としてインデックスに「2重に」追加してしまってもかまいません。 検索結果が日付順に表示されているかぎり、必ず新しい文書が先に表示されます。
indexer.py
コマンドのデフォルトのふるまいは、
インデックスの最終更新時刻 (= インデックスファイルのうち最後のものが更新された時刻)
よりも新しいファイルのみをインデックスに追加します。
そのため、普通にすべてのファイルを indexer.py
に与えれば、
古いファイルは自動的に無視され、更新されたファイルだけがインデックスに追加されます。
特別な操作は何も必要ありません。
ある少数の決まった文書だけが頻繁に更新される場合は、 あらかじめそれ以外の文書をインデックスしておき、 頻繁に更新される文書だけを毎回再インデックスすることができます。 これはインデックスファイルに異なるプレフィックスを使うことによって実現できます。
indexer.py
および merger.py
コマンドでは、-p
オプションによって
作成するインデックスファイルのプレフィックス (アルファベット3文字、通常は idx
) を指定できます。
たとえば:
aaa
で始まるインデックスファイルにする。(これは一回だけ作成)
bbb
で始まるインデックスファイルにする。(これは毎回作り直し)
こうしておくと、検索時には bbb
で始まるインデックスファイルが
必ず先に参照されるため (辞書順で大きいインデックスファイル名が先に使われる)、
頻繁に更新される文書のほうがつねに静的な文書よりも上位にランキングされることになります。
実際のコマンドラインは以下のようになります:
(最初の 1回だけ実行) $ cat ほとんど更新されない文書の一覧 | sortbymtime.py | indexer.py -paaa /tmp/index/ (定期的に実行) $ cat 頻繁に更新される文書の一覧 | sortbymtime.py | indexer.py -R -pbbb /tmp/index/
tools/
ディレクトリに含まれている sortbymtime.py
コマンドは、
標準入力から与えられたファイル名をその更新日時の順に並べかえるものです。
(indexer.py
は最後にインデックスされた文書を最初に表示するので、
日付の新しい順にランキングするにはこのようにする必要があります。)
2番目の形式にある -R
オプションは、
既存のインデックスファイルをすべて無視してゼロからインデックスを作りなおす
(「追加」しない) よう指定します。頻繁に更新される文書が 10程度の場合、
すべてを再インデックスしても数秒しかかかりません。
さらに、find
コマンドにある -mtime
オプションを使うと、
頻繁に更新される文書の一覧をほぼ自動的に発見できます:
(どちらも定期的に実行) $ find -name '*.html' -type f -mtime +3 | sortbymtime.py | indexer.py -paaa /tmp/index/ $ find -name '*.html' -type f -mtime -3 | sortbymtime.py | indexer.py -R -pbbb /tmp/index/
この例では 3日以上更新されないファイルを「古い文書」としてインデックス aaa
に追加し、
3日以内に更新されたファイルはインデックス bbb
に登録されることになります
(bbb
は毎回作り直されます)。
それまで更新されていたファイルが 3日以上更新されなくなると、
これは自動的に aaa
のほうに追加されることになります。
Fooling は、(活発な Wiki のように) 不特定多数の文書が随時更新されているようなサイトの検索には向きません。 このような場合は、定期的に全インデックスを作り直すしかないでしょう。
何らかの事情によりインデックスファイルを削除したい場合は、 番号の低い (古い) 方から削除してください。 個々のインデックスファイルは独立しているため、 古いインデックスファイルを削除しても、最近インデックスされた 文書はひきつづき検索できます。
また、インデックスファイルを削除したあとに
そのディレクトリに対して merger.py
コマンドを実行すると、
インデックスファイルの名前は 0 からの連番になるよう自動的に変更されます。
(以下の時間はすべて Linux-2.4.31、Pentium 4 (1.8GHz)、メモリ 512 MBytes、7200rpm の IDEディスク 1基で測定したものです)
種類 | 文書数 | 文書の大きさ | インデックス生成時間 | インデックスの大きさ |
---|---|---|---|---|
新聞記事 (プレインテキスト形式、日本語): | 788,810件 | 1215 MBytes | 630分 (1200文書/分) | 622ファイル、4577 MBytes |
電子メール (RFC2822形式、日本語および英語): | 91,200件 | 775 MBytes | 70分 (1300文書/分) | 70ファイル、270 MBytes |
Java 1.5 日本語ドキュメント (HTML形式、euc-jp): | 9,700件 | 220 MBytes | 38分 (250文書/分) | 13ファイル、110 MBytes |
Python 2.4 日本語ドキュメント (HTML形式、euc-jp): | 1,400件 | 14 MBytes | 3分 (450文書/分) | 3ファイル、14 MBytes |
Linux-2.4 ソースコード (テキスト形式、英文): | 12,700件 | 120 MBytes | 28分 (500文書/分) | 23ファイル、116 MBytes |
インデックス時間は HTML や MIME ヘッダの解析も含みます。 インデックス時の最大メモリ使用量は 20〜100MBytes です。 なお電子メールは新山の個人宛のもので、 spam を含む約3年分のメッセージを使用しています。 新聞記事は 2006年における日本の新聞サイト約25紙を独自にクロールして取得したものです。
検索内容 | 最初の 10件を表示 | 全件を表示 (1回目) | 全件を表示 (2回目) |
---|---|---|---|
Java ドキュメントから を検索 (4157件) |
0.2秒 | 56.2秒 | 32.7秒 |
Java ドキュメントから を検索 (3360件) |
0.5秒 | 73.7秒 | 58.4秒 |
Java ドキュメントから を検索 (47件) |
1.1秒 | 4.1秒 | 2.1秒 |
Java ドキュメントから を検索 (0件) |
0.5秒 | - | - |
Python ドキュメントから を検索 (786件) |
0.3秒 | 18.4秒 | 13.2秒 |
Python ドキュメントから を検索 (24件) |
0.2秒 | 0.7秒 | 0.3秒 |
Python ドキュメントから を検索 (12件) |
0.2秒 | 0.4秒 | 0.3秒 |
Python ドキュメントから を検索 (1件) |
0.1秒 | - | - |
Linux ソースコードから を検索 (550件) |
0.1秒 | 11.1秒 | 0.7秒 |
Linux ソースコードから を検索 (124件) |
0.6秒 | 10.0秒 | 1.0秒 |
検索時間は snippet (該当部分の文脈) 生成・表示までを含みます。 OS のバッファキャッシュにより、通常 2回目以降の検索は多少速くなります。
Fooling のインデックスファイルには、以下のような制約があります:
ひとつのインデックスファイルには、(文書の大きさにもよりますが) おそらく最大数十万件〜百万件の文書を格納できます。 そのため原理的には数十億の文書を扱えることになりますが、実際にはインデックスファイルは 逐次的に検索されるので、あまりインデックスファイルの数を増やすことはおすすめしません (数百個程度が無難です)。おそらく文書数 1000万件ぐらいが実用になる限界でしょう。 しかし「快適に使える」レベルはもっと少なく、たかだか 100万件程度だと思われます。 まだそこまで試していませんけど。
きっとまだ山のようにあるはずです (2006年9月7日現在)。 怪しい挙動を発見した際はぜひ作者までお知らせください。
できるだけこちらにお願いします: http://groups.google.com/group/fooling-users
新山 祐介
(yusuke at cs dot nyu dot edu
)
(いわゆる MIT/X ライセンスです、改変・再配布および商品への組み込みはご自由に)
Copyright (c) 2006-2008 Yusuke Shinyama <yusuke at cs dot nyu dot edu>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.