関口宏司のLuceneブログ

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

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

| スポンサードリンク | - | | - | - |
IndexWriterのautoCommitパラメータ
これはまだtrunkバージョンの話だが、IndexWriterにautoCommitモードなるものが追加された。

autoCommitはtrue|falseのどちらかをIndexWriterを作成するときのコンストラクタで指定する。デフォルトはtrueでLucene 2.1以前の動作と同じとなる。

ここでいうコミットとは、segments_Nファイルを書き込んでセグメントを確定させることを指す。

Lucene 2.0以前ではsetMaxBufferedDocs()(デフォルトは10)で指定したドキュメント数に達したときにメモリからインデックスにドキュメントが書き出されてsegments(当時は"_N"はつかなかった)にも反映されたので、この時点でIndexReaderをオープンすれば、インデックスに書き出されたドキュメントは検索にも反映された。

Lucene 2.1では、IndexWriterにflush()というメソッドが追加され、これはsetMaxBufferedDocs()にドキュメント数が達しない場合でも、flush()により強制的にメモリからインデックスにドキュメントを書き出させるものである。これにより、書き出しのタイミングコントロールがプログラムからできるようになった。

trunkバージョンではさらにautoCommit=falseを指定することができるようになり、この場合、メモリからインデックスにドキュメントが書き出されてもsegments_Nを書き換えない。そうすることによりIndexReaderにはコミット前の古いセグメントを見せることができる。そしてautoCommit=falseを指定した場合に限り、IndexWriterをclose()またはabort()することを選択できる。close()はもちろん、IndexWriterを閉じてaddDocument()を確定させる(従来どおり)。abort()はやはりtrunkバージョンで加わったメソッドで、これを呼ぶとそれまでのaddDocument()はすべて破棄され、セグメントは古いままで更新されない。
| 関口宏司 | Luceneインデックス | 15:35 | comments(0) | trackbacks(0) |
お手軽なメモリ節約法
ドキュメントを構成するあるインデックスフィールドのnorm factorが不要な場合、当該フィールドに対してsetOmitNorms(true)を指定すると、検索時のヒープ消費量が最大で「ドキュメント数分のバイト」だけ節約できる。

たとえば、インデックスに100万件のドキュメントがあるとき、1つのインデックスフィールドに対してsetOmitNorms(true)を実行すると、そのフィールドを検索するプログラムではsetOmitNorms(true)を実行しない場合と比べて1Mバイト(ここではMは百万)のメモリが節約できる。これはLuceneでは検索のたびにnorm factorをインデックス(.nrmファイルに相当)から読むのではなく、一度読んだnorm factorをメモリ上にキャッシュするためである。

このキャッシュはフィールド名をキーにしたテーブルで管理されているため、setOmitNorms(true)を行ったフィールドの分だけ節約の効果が出てくる。たとえば、5つのインデックスフィールドでsetOmitNorms(true)を実行すると、100万件の文書では5Mバイト、1,000万件の文書では50Mバイトのヒープが節約できる。

文書数が大きなインデックスほど節約効果が大きく簡単に試せるため、覚えておくとよいだろう。
| 関口宏司 | プロとして恥ずかしくないLuceneの大原則 | 22:53 | comments(0) | trackbacks(0) |
異なるバージョンのインデックスを同時検索するプログラム
気のせいかもしれないが、最近、「飛び石連休」という言葉を聞かなくなった。昔は確か5月4日は休日ではなく、したがって5月3日の憲法記念日と5月5日のこどもの日は分離された休日であり、これに4月29日の(現)昭和の日とが加わって毎年の土日の組み込まれ方によってはあたかも飛び石のように飛び飛びに休日と平日が並ぶためにそのように呼ばれたものである。それが5月4日も休みになったことにより、飛び石と呼ぶには少し飛び方が少なくなったために「飛び石連休」という言葉が使われにくくなったことが関係あるのかもしれない。たとえば今年は5月1日と2日がカレンダー上の平日となり、連休の前半と後半という呼び方がされているようだ。今年は私の周りでは、ある会社はこの2日間を休みとして9連休とするところもあれば、忙しいプロジェクトに組み込まれた人らは連休って何?というメンバーもいるようである。

そんな連休の谷間の5月2日、弊社は静かに設立2年目を迎えた。最近でこそ仕事内容はLuceneとSolrの一色になっているが、設立当初はなかなかそれだけというわけにはいかず、AntやSubversionなどのコンサルも手がけたものである。

Subversionのコンサルといえば、よく下のような図を示してソースコード管理をタイムマシンになぞらえた話をした:

Subversionによる複数バージョンのソース管理

ソースコード管理にタイムマシンの話を持ち込むのは別に私のオリジナルではないので聞いたことがある人も多いであろうが、過去の特定のリビジョンに戻ったり(残念ながらこのタイムマシンは過去にしかいけない、まだ見ぬ未来(将来バージョン)は自分たちの手で創造しなければならないのだ)、別の歴史を枝分かれさせてそのトランク(幹)を覘くこともできるというたとえ話である。

ところで最近、Solrを調べていてインデックスのバージョンが上がる様子を説明するのに、似たような図を用いた。次のような図である:

Solrによる複数Searcherの管理

これは頻繁にアップデートがされるインデックスにおいて、複数のSolrIndexSearcher(LuceneのSearcherの拡張)が存在し、Solrフレームワークで管理されていることを示した図である。ある瞬間では、検索に使用されるのは図の一番下に位置するSolrIndexSearcherのみであり同時に複数のSolrIndexSearcherが検索に使用されることはない。また、2番目3番目に控えているSolrIndexSearcherはこの順番どおりにウォーミングアップがなされ、"registered"になると「現在」のSolrIndexSearcherとなって検索に使用されるようになる。そしてそれまで検索に使用されていたSolrIndexSearcherは参照されなくなり、やがて捨てられる。したがってこの場合タイムマシンのような話をするのは適当ではないが、バージョンがあがっていくインデックスに対して枝分かれしたSearcherが存在するのは確かなことであり、図で表すとすればやはり上図のようなものになるだろう。

Solrではバージョンがあがっていくインデックスに対して複数のSearcherが存在しえるが、ある瞬間に検索で使用されるのは一番古いバージョンを参照しているSearcherである。これはSolrのフレームワークによってコントロールされているが、そうでない場合は使用するSearcherのインスタンスによって、同じプログラムから違った検索結果を得られる可能性があるということを意味する。これはちょっと興味深い話だ。

そこでバージョンがあがっていくインデックスに対して、そのときどきのバージョンに対するSearcherが存在する様子を見られるLuceneプログラムを作成してみよう。これはとても簡単で、下のようなものである:



public class MultiSearchers {

private static final String INDEX = "index";
private static Analyzer analyzer = new WhitespaceAnalyzer();
private static final String F = "f";
private static final String TERM = "java";

public static void main(String[] args) throws IOException {
addDocument();
IndexSearcher searcher1 = new IndexSearcher( INDEX );
addDocument();
IndexSearcher searcher2 = new IndexSearcher( INDEX );
addDocument();
IndexSearcher searcher3 = new IndexSearcher( INDEX );

search( searcher1, "searcher1" );
search( searcher2, "searcher2" );
search( searcher3, "searcher3" );
searcher1.close();
searcher2.close();
searcher3.close();
}

private static void addDocument() throws IOException{
IndexWriter writer = new IndexWriter( INDEX, analyzer );
Document doc = new Document();
doc.add( new Field( F, TERM, Store.YES, Index.TOKENIZED ) );
writer.addDocument( doc );
writer.close();
}

private static void search( IndexSearcher searcher, String desc ) throws IOException{
Query query = new TermQuery( new Term( F, TERM ) );
Hits hits = searcher.search( query );
int l = hits.length();
System.out.println( desc + " : hits count = " + l );
}
}



このプログラムでは、赤字の部分でインデックスに次々と更新をかけていき、そのときどきのバージョンのIndexSearcherを取得している。更新は3回行い、それぞれの更新後にIndexSearcherを取得している。その変数名はsearcher1〜searcher3であり、最後にまとめてこれらのIndexSearcherを使い検索を行っている。

インデックスには"java"という単語を1つもつ文書を更新1回につき1文書索引付けしている。検索時には"java"という単語を検索しているので、searcher1〜searcher3を使用した検索ではヒット件数が異なるはずである。

実行結果は次のように期待通りのものが得られる。つまり1つのプログラム上で3つのバージョンのインデックスを参照している3つのIndexSearcherが同居しているということである:



searcher1 : hits count = 1
searcher2 : hits count = 2
searcher3 : hits count = 3



なお、IndexWriterのインスタンスを作成するのにLucene 2.1から導入された新しいコンストラクタを使用しているので、このプログラムを繰り返し実行すると、文書がインデックスに追加されていく。たとえば、2回目の実行結果は1回目とは異なり、次のようになるので連続実行の際には注意が必要だ:



searcher1 : hits count = 4
searcher2 : hits count = 5
searcher3 : hits count = 6


| 関口宏司 | Lucene自由自在 | 14:30 | comments(0) | trackbacks(0) |
+ Solrによるブログ内検索
+ PROFILE
  12345
6789101112
13141516171819
20212223242526
2728293031  
<< May 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