関口宏司のLuceneブログ

OSS検索ライブラリのLuceneおよびそのサブプロジェクト(Solr/Tika/Mahoutなど)について
スポンサーサイト

一定期間更新がないため広告を表示しています

| スポンサードリンク | - | | - | - |
Luceneブログ内検索デモのリニューアル
このブログページの右上にある「検索窓」の検索機能は、以前よりLuceneで作成したものを提供していたが、本日よりSolrに切り替えた。

この(Powered by RONDHUITと記してある)検索窓から検索すると、ブログサービスプロバイダ提供による検索機能ではなく、Solr(昨日まではLucene)を使って検索し、検索結果を表示するようになっている。

ブログサービスプロバイダ提供による検索機能はMovable Typeをはじめとして使いにくいものが多く、LuceneやSolrを使って専用の検索機能を設置することはユーザ(ブログ読者)の利便性を高めるのに役立つ。

ためしにこのページの右下にあるもうひとつの検索窓を使って適当な検索語(たとえば「Lucene」など)を検索してみて欲しい。この検索窓はブログサービスプロバイダ提供による検索機能であるが、そのレスポンスは検索語が含まれる記事がベタに出力されるだけのものであり、使いにくいことが実感できると思う。

デモシステムの概要

今回リニューアルしたブログ内検索デモのシステム構成を下図に示す。

デモシステム構成

「ブログ内検索」といいつつ、今回は私の会社のホームページも検索対象に含めている。つまり、ひとつの検索窓から検索すると検索結果ページには「ブログ記事」と「ロンウイットホームページ」のURLが混在して表示されるのだ。ブログと会社のホームページを「横断検索」できる、と言い換えてもよい。

・・・と書きながら今気がついたが、ブログと会社のホームページが横断検索できるのであれば、「検索窓」がブログの方にだけあるのは片手落ちではないか。会社のホームページの方にも横断検索のための検索窓があってしかるべきではないのか。まったくそのとおりなので、こちらは年明けにでもデザイナーさんにお願いしようと思う。
(なお、全文検索の会社のくせに会社のホームページに「検索窓」が設置していないことについて、前バージョンのデモを紹介しているこちらの記事にその理由が書かれている)

なお、今回のデモシステムではフロントAPサーバにはApache+FastCGI上でRailsのアプリケーション稼動させている。

デモシステムの新機能

今回のデモでは単にLuceneをSolrに置き換えたり、フロントにRailsを使用したりといった構成の変更だけではなく、次のような機能向上を取り入れている。

類義語検索

昨日のことであるが、私は近所のスターバックスに行き「本日のコーヒーのショートサイズ」を注文して店内で飲みながら「スタバではグランデを買え!」(吉本佳生著)を読んだ。

別に意識してそうしたわけではなく、私はスターバックスでは「本日のコーヒーのショートサイズ」を注文することと決まっているのだった。

コーヒー専門店であるスタバにはさまざまなコーヒーの種類やさらにはトッピングなるものが用意され、お客様の好みに合わせたコーヒーのバリエーションでおもてなしをしてくれるようになっている。私にはそれが恐ろしい。スタバには2000年ごろから親しんでいる私であるが、メニューからいろいろ選ぶことができないまま数年が過ぎ、今では「本日のコーヒーのショートサイズ」とレジ前でいうことが習慣として定着している。

突然何の話かと訝ると思うが、類義語の話の前フリである。上の文章では(実話である)「スターバックス」と「スタバ」を同じ意味で使っている。キーワード検索ではこれらを「類義語」として扱うようにできていないと、ユーザは検索窓に「スターバックス OR スタバ」などと入力しないと両方を検索できないため大変不便だ。今回のデモではこの類義語検索ができるようになっており、検索語のハイライトも可能である。

類義語の検索のためには類義語辞書を整備する必要があるが、デモでは現時点で次のような類義語リストを登録している:



ロンウイット, RONDHUIT, 丸八
アマゾン, amazon
アドビ, アドビシステムズ, adobe
イーモバイル, EMOBILE
リクルート, RECRUIT
レッドハット, redhat, red hat
マクドナルド, マック, マクド
スターバックス, スタバ, starbucks

コンサル, コンサルティング
コラボ, コラボレーション
コンファレンス, カンファレンス
ミーティング, 会議
ブログ, ウェブログ, weblog, blog
ウィンドウ, ウインドウ, window
エンタープライズ, enterprise
ミドルウェア, middleware
デモ, demo
ソースコード, プログラム
ページ, 頁

インデクシング, インデキシング, indexing
スループット, QPS
強調表示, ハイライト, highlight, highlighter
トランク, trunk
ドキュメント, 文書
サーチ, 検索
絞込み, 絞込, 絞り込み
エラー, error
なまず, ナマズ, 鯰, namazu
セナ, senna



たとえば、弊社名「ロンウイット」をアルファベットの「RONDHUIT」で検索したりその逆も可能である。「コンサルティング」で「コンサル」が検索できたりするので便利なのがわかるだろう。

絞り込み検索

これまでのデモシステムでも絞り込み検索は可能であるとしていたが、今まではブログのカテゴリや日付の範囲でどちらか一度絞り込み検索を行うと、その先さらに絞込みを行うことができなかった。

新しいデモでは、絞り込み検索のリンクをクリックして絞り込まれた結果を、さらに別のリンクをクリックして次々と絞り込んでいくことが可能となっている。

トップページのナビゲーションリンクの表示

トップページが次のようになった。

デモトップ画面
http://www.rondhuit-demo.com/lbs2demo/


こうやって客観的に見るとつくづく地味な画面だ(これも年明けデザイナーに頼んで何とかしよう)。それはともかく、図にあるようにトップページにはあらかじめ、検索窓に検索語を入力しなくても検索して絞り込んでいけるようにナビゲーションリンクが表示されるようになった。このリンクにはLucene学習者向けに、Luceneの基本クラスなるものを私があらかじめピックアップし、これらのクラスが含まれる記事が簡単に検索できるようにもなっている。なおこれらは検索語ではなくフィルタとして扱われるため、ハイライトの対象とはならないことを注記しておく。

また旧デモでは「最近検索されたことば」なるものがリンクで表示されていたが、検索キーワードのモニタリング機能は現在開発中のため、今回のデモからは一時的にはずしてある。
| 関口宏司 | Luceneデモ | 15:23 | comments(0) | trackbacks(1) |
optimize( maxNumSegments )
Lucene 2.3(現trunk、リリースは来年1月ころ?)のIndexWriterクラスに最大セグメント数を引数に取るoptimize()メソッドが新たに追加された。

従来の引数を取らないoptimize()メソッドは、インデックスの複数のセグメントを1つにまとめる働きがある。このとき、インデックスファイルが巨大だとoptimize()にもそれなりに時間がかかる。

マージの時間は取られるが、セグメントを最適化して1つにまとめることは検索速度を最大に保つためだ。しかし、インデックスが大きくなればなるほど、マージの時間がかかってしまうのが悩ましいところである。

今回追加された新しいoptimize()メソッドにはマージ後の最大セグメント数を指定できるようになっている。たとえばoptimize( 10 )と指定すると、マージがセグメントが10個になるまで行われ、10個以下に達するとマージはそこでおしまいとなる。

今回追加されたoptimize()メソッドを使うことで「検索速度はまあそこそこに、マージは早く終わらせたい」アプリケーションで効果がある。
| 関口宏司 | Luceneクラス解説 | 10:43 | comments(0) | trackbacks(0) |
Tokenの再利用で性能向上
まもなくリリースされるLucene 2.3の改善点に、文字列分析時のTokenオブジェクトの再利用というものがある:

LUCENE-969
http://issues.apache.org/jira/browse/LUCENE-969

この改善点の主旨は、主にインデクシング時に大量に生成されるTokenオブジェクトを、従来はnewしていたが、これをTokenStream内で再利用することで余分なオブジェクト生成を減らし、さらにGCも減らして性能向上を狙ったものである。

このため、TokenStreamの従来のnext()メソッドに加え、next(Token)メソッドが新設された。この引数には使い終わったTokenオブジェクトを渡す。

Tokenクラスの方では、これまでトークン文字列を保持するのにStringを使用していたがこれをdeprecatedとし、char[]とトークン文字列の長さをあらわすintで持つように変更された(Stringのままではオブジェクトを作成してしまうため)。この変更により、Stringへのaccessorメソッドはdeprecatedとなり、char[] termBuffer()やint termLength()などが新設された。

さらにこれらとは直接関係しないが、多数のFieldやDocumentの分析中にTokenStreamを再利用するためのreusableTokenStream()というメソッドがAnalyzerに新設された。

Tokenオブジェクトの再利用でどのくらいの性能改善が達成されるかを計測してみたところ、1億個の単語(単語長は1〜20で変化)の分析で次のような結果となった:

処理時間 [ms]GC
回数時間 [s]
再利用34,78990.03
再利用なし57,1096120.4


処理時間で39%、GC時間では1/10以下もの性能が達成されていることが確認できた。
| 関口宏司 | Luceneクラス解説 | 09:55 | comments(0) | trackbacks(0) |
JDiffでAPIの変更点を知る
現在、Lucene 2.3リリースに向けての作業が急ピッチで進められている。新バージョンリリース時にはAPIの変更点がプログラマとしては気になるところだろう(もちろん、後方互換性には最大限注意が払われている)。

そんな中、私は次のチケットで初めて知ったが、JDiffというツールが紹介された:

http://issues.apache.org/jira/browse/LUCENE-1083

JDiffはJavaで書かれたプログラムの2つのリリースを、Javadocコメントを比較することでAPIの差異を表示してくれるツールである。表示形式は次のようにJavadocのようなHTMLで行われる:

JDiff出力例

上図はLucene 1.9.1とLucene 2.2.0のIndexWriterの差異の一部を示している。

JDiffの仕組みは少し古いがJava Developers Journalの記事(PDF)で紹介されている。

APIの差異を一覧するのに大変重宝しそうなツールである。
| 関口宏司 | Luceneクラス解説 | 08:25 | comments(0) | trackbacks(1) |
FieldCacheがクラスに変更!?
現在、後方互換性のルールを破ってしまう変更が検討されており、メーリングリストで非公式に呼びかけが行われている。内容は、FieldCacheをインタフェースからクラス(または抽象クラスの可能性もまだあるようだ)にしてしまう、次のような変更である:


* @since lucene 1.4
* @version $Id$
*/
-public interface FieldCache {
+public class FieldCache {


この変更は、long値を索引付けしたフィールドに対し、SortField.AUTOを指定してソートしたときに正しくソートできない不具合に対応するために行われる:

http://issues.apache.org/jira/browse/LUCENE-1045

この不具合の修正は、一旦は次のようにフィールドを「(1)intでパース、(2)失敗したらfloatでパース、(3)さらに失敗したらStringでパース」と従来はなっていたものに、(1)と(2)の間にlongでパースするロジックをFieldCacheインタフェースの標準実装であるFieldCacheImpleクラスに挿入することでコミットされた:


@@ -455,10 +501,15 @@
ret = getInts (reader, field);
} catch (NumberFormatException nfe1) {
try {
- Float.parseFloat (termtext);
- ret = getFloats (reader, field);
+ Long.parseLong(termtext);
+ ret = getLongs (reader, field);
} catch (NumberFormatException nfe2) {
- ret = getStringIndex (reader, field);
+ try {
+ Float.parseFloat (termtext);
+ ret = getFloats (reader, field);
+ } catch (NumberFormatException nfe3) {
+ ret = getStringIndex (reader, field);
+ }
}
}
} else {


しかし、よりスマートなやり方として前述の別解が提案されたものである。

・・・とちょうどここまで書いていたところで、別のコミッターから「互換性を捨ててまでクラスにする必要はないんじゃないか」という意見も出され、リアルタイム進行中である。
| 関口宏司 | Luceneクラス解説 | 09:44 | comments(0) | trackbacks(0) |
IndexReader.reopen()
IndexReaderクラスのreopne()メソッドは、もうじきリリースされるとうわさされているLucene 2.3の新機能である。これまでインデックスをオープンするのにIndexReader.open()メソッドしかなかったが、reopen()が加わった。アプリケーションでreopen()を使うことで、open()メソッドよりも性能改善が見込める。

使い方は、reopen()はインスタンスメソッドなので(open()はクラスメソッドである)、既存のIndexReaderオブジェクトに対してreopen()を呼び出し、新しいIndexReaderオブジェクトを得る。古いIndexReaderオブジェクトはその後close()をする。

インデックスが更新されたら、検索結果に更新分を反映させるためにこれまではIndexReader.open()を呼んで新しいIndexReaderオブジェクトを取得する必要があった。

Lucene 2.3では新しいIndexReaderを取得するのにreopen()が使える、ということである。reopen()はインデックスの更新差分のみロードするので性能が改善するのである。

どのくらい改善するのか測定してみたところ、次のようになった。

プログラムはランダムな単語が100万単語ずつ増えていくインデックスから任意の1単語を100万単語増えるごとに1回、計5回検索したときの合計タイムを計測するものである(したがってプログラム終了時はインデックス内には500万単語が存在する)。

open [ms]reopen [ms]
1回目165125
2回目190126


2回ずつ計測してみたが、24%から34%程度reopen()の方が速くなっていることがわかる。
| 関口宏司 | Luceneパフォーマンス | 07:55 | comments(0) | trackbacks(0) |
+ Solrによるブログ内検索
+ PROFILE
      1
2345678
9101112131415
16171819202122
23242526272829
3031     
<< December 2007 >>
+ LINKS
検索エンジン製品 - 比較のポイント
商用検索エンジンを購入した企業担当者は読まないでください。ショックを受けますから・・・
>>製品比較 10のポイント
+ Lucene&Solrデモ
+ ThinkIT記事
+ RECOMMEND
Apache Solr入門 ―オープンソース全文検索エンジン
Apache Solr入門 ―オープンソース全文検索エンジン (JUGEMレビュー »)
関口 宏司,三部 靖夫,武田 光平,中野 猛,大谷 純
+ RECOMMEND
Lucene in Action
Lucene in Action (JUGEMレビュー »)
Erik Hatcher,Otis Gospodnetic,Mike McCandless
FastVectorHighlighterについて解説記事を寄稿しました。
+ RECOMMEND
+ SELECTED ENTRIES
+ RECENT COMMENTS
+ RECENT TRACKBACK
+ CATEGORIES
+ ARCHIVES
+ MOBILE
qrcode
+ SPONSORED LINKS