RubyのFerretライブラリによるドキュメントのインデックス作成と検索
現時点で、Ferretと呼ばれるRuby gemが既にインストールされているはずです。Ferretは、Java Luceneをベースとした高速のインデックス作成および検索ライブラリです(Common LispのバージョンMontezumaよりもはるかに高速です)。Ferretライブラリは、その開発において、作者であるDavid BalmainがそのほとんどをCで開発しRubyでラップしたというおもしろい経緯があります。これは、つまり、Rubyで開発を始めてパフォーマンスに問題が生じた場合は、処理速度が重視される部分を常にCまたはC++で再コーディングできるということです。Ferretでは、Rubyを使用した場合に自分のアプリケーション内で使えるクラスがいくつか定義されています。以下に例を示します。
- Document
- Field
- Index
- Query
Microsoft Word文書のインデックス作成と検索
以下は、Microsoft Word文書を読み取ってプレーンテキストを抽出するために私が使用しているRubyクラスです。Rubyで外部プログラムを使用する例として紹介します。
class ReadWordDoc attr_reader :text def initialize file_path # back quotes to run external program @text = `antiword #{file_path}` end end
この「裏技」は、私がオープンソースのAntiwordユーティリティを使って実際にWord文書ファイルを処理しているものです。外部コマンドをバッククォートで囲むと、外部プログラムを実行してその結果を文字列に出力することができます。LinuxまたはOS Xの場合は次のコマンドを実行してください(Windowsの場合は`dir`)。
puts `ls -l`
この例では、外部コマンドls
(Unixのディレクトリ表示コマンド)を実行した結果が出力されます。
次のRubyスクリプトを実行すると、Word文書がインデックスに入力されます(プレーンテキストファイルはより簡単です。練習として試してみてください)。
require 'rubygems' require 'ferret' include Ferret include Ferret::Document require 'read_word_doc' # read_word_doc.rb defines class ReadWordDoc # any path to a directory index = Index::Index.new(:path => './my_index_dir') # path to a Microsoft Word doc_path = 'test.doc' # get the plain text from the Word file doc_text = ReadWord.new(doc_path).text doc = Document.new doc << Field.new("doc_path", doc_path, Field::Store::YES, Field::Index::NO) doc << Field.new("text", doc_text, Field::Store::YES, Field::Index::TOKENIZED) index << doc # a test search index.search_each('text:"Ruby"') do |doc, score| # print doc_path meta data puts "result: #{index[doc]['doc_path']} : #{score}" # print original text puts "Original text: #{index[doc]['text']}" end index.close # close the index when you are done with it
このサンプルコードがいかに短いか分かると思います。Word文書からテキストを抽出するAntiwordを使うためのクラスを含めても、わずか24行で、Wordからテキストを抽出してインデックスを作成し、検索を実行して終了したらインデックスをクローズするというサンプルを作成できるのです。
Rubyを使うと、複雑な作業をほんの数行のコードで処理することができます。Javaでこのようなサンプルコードを作成した場合、(私が作った)非常によくできたLuceneライブラリを使ったとしても、行数ははるかに長くなるでしょう。プログラムは短ければ短いほど、メンテナンスが簡単になり、そのコストも低減されます。
この例では、Word文書を使いましたが、OpenOffice.org文書も読み取り可能です。約30行の純粋なRubyコードで、ドキュメントをunzipし、unzipされたXMLデータストリームのcontent.xml要素からテキストを抽出できます(RubyではXMLも簡単に処理できますが、本稿では触れません)。
RubyによるJavaの補完
通常、企業のIT予算においてソフトウェアの開発とメンテナンスにかかるコストは莫大なものです。サーバやインターネット接続などにかかるコストの比ではありません。しかし、Rubyを使うことで、通常はプログラムの長さ自体が短くなるため、システムの構築およびメンテナンスのコストを大いに削減できます(私の場合、コード1行にかかる時間は、どのプログラミング言語でもほとんど差はありません)。
では、Javaはどのような場面で使えばよいのでしょうか? 私の場合は、10年以上コンサルティングしているお客様のシステムを構築するときにJavaプラットフォームを使用したので、これからも確実にJavaを使い続けるでしょう。堅牢なJavaベースのWebアプリケーションは、少なくともサーバがダウンしたりハードウェアメンテナンスで再起動したりする場合以外は、いつまでも稼働するでしょう。実際に数か月間も無人状態で問題がなく稼働するシステムを確認しています。
私の意見としては、大規模なシステムのサーバサイドではJavaを使い続け、小規模なユーティリティプログラムでRubyを使い始めるのが良いと思います。私は、JavaとRubyは互いに競合するのではなく、互いに補完するものと考えています。作業に合わせて最適なツールを使うことをお勧めします。