一定期間更新がないため広告を表示しています
関口宏司のLuceneブログOSS検索ライブラリのLuceneおよびそのサブプロジェクト(Solr/Tika/Mahoutなど)について
2009.07.31 Friday
contrib/benchmarkを使う
Luceneのcontribにはbenchmarkというプロジェクトがあり、繰り返しLuceneの特定のクラスをテストし、性能比較をしたりするのに便利に使える。benchmarkは、「アルゴリズム」と呼ばれるスクリプトファイル(拡張子が".alg")を用意し、次のようにAntからアルゴリズムを指定して起動する:
$ cd contrib/benchmark $ ant -emacs run-task -Dtask.alg=script.alg $ ant -emacs run-task -Dtask.alg=conf/standard.alg -Dtask.mem=512M (途中大いに省略) ------------> Report sum by Prefix (MAddDocs) and Round (8 about 8 out of 160154) Operation round mrg buf cmpnd runCnt recsPerRun rec/s elapsedSec avgUsedMem avgTotalMem MAddDocs_20000 0 10 10 true 1 20000 383.49 52.15 9,933,048 10,702,848 MAddDocs_20000 - 1 100 10 true - - 1 - - 20000 - 1,067.75 - - 18.73 - 14,436,664 - 23,080,960 MAddDocs_20000 2 10 100 true 1 20000 1,910.95 10.47 20,653,840 24,236,032 MAddDocs_20000 - 3 100 100 true - - 1 - - 20000 - 1,997.60 - - 10.01 - 15,501,824 - 26,984,448 MAddDocs_20000 4 10 10 false 1 20000 824.06 24.27 15,647,864 31,096,832 MAddDocs_20000 - 5 100 10 false - - 1 - - 20000 - - 845.34 - - 23.66 - 28,066,896 - 34,091,008 MAddDocs_20000 6 10 100 false 1 20000 1,247.82 16.03 18,228,480 34,091,008 MAddDocs_20000 - 7 100 100 false - - 1 - - 20000 - 1,498.02 - - 13.35 - 30,658,152 - 35,840,000 #################### ### D O N E !!! ### #################### $ # 1. 各種プロパティの設定 directory=FSDirectory : # 2. ベンチマークスクリプト本体 { : } # 3. レポートの出力 RepAll :
# simple1.alg # デフォルトのRAMDirectoryではなく、インデックスをファイルに出力 directory=FSDirectory # インデックスを新規に作成し、ドキュメントを1件登録し、インデックスをクローズ { CreateIndex AddDoc CloseIndex } $ ant -emacs run-task -Dtask.alg=myalg/simple1.alg -Dtask.mem=512M : # ロイターのデータがダウンロードされる get-reuters: Getting: http://www.daviddlewis.com/resources/testcollections/reuters21578/reuters21578.tar.gz : ------------> config properties: directory = FSDirectory work.dir = work ------------------------------- ------------> algorithm: Seq { CreateIndex AddDoc CloseIndex } #################### ### D O N E !!! ### ####################
# simple2.alg directory=FSDirectory { CreateIndex AddDoc CloseIndex } # レポートを出力 RepAll $ ant -emacs run-task -Dtask.alg=myalg/simple2.alg -Dtask.mem=512M : # ロイターのデータはすでにダウンロードされているので、実行も素早い : # レポートが出力されるようになった ------------> Report All (4 out of 5) Operation round runCnt recsPerRun rec/s elapsedSec avgUsedMem avgTotalMem Seq 0 1 3 21.43 0.14 1,058,784 2,031,616 CreateIndex - 0 - - 1 - - - - 1 - - 12.82 - - 0.08 - - 557,944 - - 2,031,616 AddDoc 0 1 1 27.03 0.04 644,336 2,031,616 CloseIndex - - 0 - - 1 - - - - 1 - - 47.62 - - 0.02 - 1,058,784 - - 2,031,616 #################### ### D O N E !!! ### ####################
# simple3.alg directory=FSDirectory { CreateIndex # { ... } : n の記法により、{ ... } の中をn回繰り返せる。この場合、ロイターの記事を2000記事追加する { AddDoc } : 2000 CloseIndex } RepAll $ ant -emacs run-task -Dtask.alg=myalg/simple3.alg -Dtask.mem=512M : ------------> Report All (2004 out of 2005) Operation round runCnt recsPerRun rec/s elapsedSec avgUsedMem avgTotalMem Seq 0 1 2002 3,575.00 0.56 1,643,608 3,129,344 CreateIndex - 0 - - 1 - - - - 1 - - 12.99 - - 0.08 - - 554,832 - - 2,031,616 Seq_2000 0 1 2000 5,181.35 0.39 1,636,640 2,031,616 AddDoc - - - 0 - - 1 - - - - 1 - - 27.78 - - 0.04 - - 644,552 - - 2,031,616 AddDoc 0 1 1 250.00 0.00 644,552 2,031,616 : (巨大なレポートが出力されてしまう)
# simple4.alg directory=FSDirectory { CreateIndex { AddDoc } : 2000 CloseIndex } # 同じ名前の行をまとめてレポートを読みやすくする RepSumByName $ ant -emacs run-task -Dtask.alg=myalg/simple4.alg -Dtask.mem=512M : ------------> Report Sum By (any) Name (5 about 2004 out of 2005) Operation round runCnt recsPerRun rec/s elapsedSec avgUsedMem avgTotalMem Seq 0 1 2002 3,537.10 0.57 1,648,936 3,129,344 CreateIndex - 0 - - 1 - - - - 1 - - 12.99 - - 0.08 - - 564,216 - - 2,031,616 Seq_2000 0 1 2000 5,128.21 0.39 1,631,912 2,031,616 AddDoc - - - 0 - 2000 - - - - 1 - 5,665.72 - - 0.35 - 1,195,525 - - 2,031,616 CloseIndex 0 1 1 10.10 0.10 1,648,936 3,129,344 #################### ### D O N E !!! ### ####################
# simple5.alg directory=FSDirectory { CreateIndex { AddDoc } : 2000 Optimize CloseIndex } # 検索を5000回実行してみる { OpenReader { Search } : 5000 CloseReader } RepSumByName $ ant -emacs run-task -Dtask.alg=myalg/simple5.alg -Dtask.mem=512M : ------------> Report Sum By (any) Name (10 about 7009 out of 7010) Operation round runCnt recsPerRun rec/s elapsedSec avgUsedMem avgTotalMem Seq 0 2 3502 5,054.11 1.39 1,911,608 3,211,264 CreateIndex - 0 - - 1 - - - - 1 - - 12.82 - - 0.08 - - 694,320 - - 2,031,616 Seq_2000 0 1 2000 5,128.21 0.39 1,714,064 2,031,616 AddDoc - - - 0 - 2000 - - - - 1 - 5,763.69 - - 0.35 - 1,199,983 - - 2,031,616 Optimize 0 1 1 10.87 0.09 1,815,056 3,211,264 CloseIndex - - 0 - - 1 - - - - 1 - - 142.86 - - 0.01 - 1,911,608 - - 3,211,264 OpenReader 0 1 1 35.71 0.03 1,911,608 3,211,264 Seq_5000 - - 0 - - 1 - - - 5000 - 6,321.11 - - 0.79 - 1,793,608 - - 3,211,264 Search 0 5000 1 6,518.90 0.77 2,090,484 3,211,264 CloseReader - 0 - - 1 - - - - 1 - 1,000.00 - - 0.00 - 1,491,848 - - 3,211,264 #################### ### D O N E !!! ### ####################
# simple6.alg directory=FSDirectory { CreateIndex { AddDoc } : 2000 Optimize CloseIndex } # 検索「ラウンド」を4回繰り返す { "Round" OpenReader { Search } : 5000 CloseReader # NewRoundをRepSumByNameRoundとともに用いて、ラウンドごとに集計したレポートを出力 NewRound } : 4 RepSumByNameRound $ ant -emacs run-task -Dtask.alg=myalg/simple6.alg -Dtask.mem=512M : ------------> Report Sum By (any) Name and Round (23 about 22018 out of 22019) Operation round runCnt recsPerRun rec/s elapsedSec avgUsedMem avgTotalMem Seq 0 1 2003 3,495.64 0.57 1,912,680 3,211,264 CreateIndex - 0 - - 1 - - - - 1 - - 12.66 - - 0.08 - - 710,776 - - 2,031,616 Seq_2000 0 1 2000 5,050.50 0.40 1,715,112 2,031,616 AddDoc - - - 0 - 2000 - - - - 1 - 5,571.03 - - 0.36 - 1,201,026 - - 2,031,616 Optimize 0 1 1 10.87 0.09 1,816,128 3,211,264 CloseIndex - - 0 - - 1 - - - - 1 - - 166.67 - - 0.01 - 1,912,680 - - 3,211,264 Round_4 0 1 20008 8,710.49 2.30 2,555,512 4,370,432 OpenReader - - 0 - - 1 - - - - 1 - - 35.71 - - 0.03 - 1,912,680 - - 3,211,264 Seq_5000 0 1 5000 6,915.63 0.72 1,799,136 3,211,264 Search - - - 0 - 5000 - - - - 1 - 7,173.60 - - 0.70 - 2,093,395 - - 3,211,264 CloseReader 0 1 1 1,000.00 0.00 1,674,656 3,211,264 OpenReader - - 1 - - 1 - - - - 1 - - 500.00 - - 0.00 - 1,712,704 - - 3,211,264 Seq_5000 1 1 5000 9,578.54 0.52 1,867,976 3,211,264 Search - - - 1 - 5000 - - - - 1 - 9,784.74 - - 0.51 - 2,240,154 - - 3,211,264 CloseReader 1 1 1 1,000.00 0.00 1,867,976 3,211,264 OpenReader - - 2 - - 1 - - - - 1 - - 500.00 - - 0.00 - 1,895,800 - - 3,211,264 Seq_5000 2 1 5000 10,141.99 0.49 2,801,496 3,211,264 Search - - - 2 - 5000 - - - - 1 - 10,482.18 - - 0.48 - 2,360,550 - - 3,211,264 CloseReader 2 1 1 1,000.00 0.00 2,801,496 3,211,264 OpenReader - - 3 - - 1 - - - - 1 - - 500.00 - - 0.00 - 2,841,344 - - 3,211,264 Seq_5000 3 1 5000 9,578.54 0.52 2,841,344 4,370,432 Search - - - 3 - 5000 - - - - 1 - 9,671.18 - - 0.52 - 2,829,369 - - 3,720,606 CloseReader 3 1 1 1,000.00 0.00 2,555,512 4,370,432 #################### ### D O N E !!! ### #################### 参考: http://hudson.zones.apache.org/hudson/job/Lucene-trunk/javadoc/all/org/apache/lucene/benchmark/byTask/package-summary.html 2009.07.29 Wednesday
ワイルドカード検索のちょっと変わった使い方
メーリングリストアーカイブ検索のnabbleやバグ管理システムのAtlassian JIRAなど、検索システムにLuceneを使用しているツールで、末尾が"s"で終わる単語を検索すると、まれに不便を感じることがある。
たとえば"solrjs"(SolrのJavaScriptクライアントライブラリ)という単語をnabbleのSolrアーカイブ内で検索してみよう: http://www.nabble.com/forum/Search.jtp?forum=14479&local=y&query=solrjs すると、"solrjs"を検索したにもかかわらず、多数の"SolrJ"(SolrのJavaクライアントライブラリ)が検索に引っかかってしまう。これは、英語圏発のこれらのツールでは、LuceneのAnalyzerにEnglish-Stemmerツールを用いており、そのツールが単語末尾の"s"を機械的に複数形の"s"とみなし、インデクシングや検索時に取り除いているためである。この働きにより、多くの場合は使いやすい検索システムとなるが、まれにこのような不都合に遭遇することとなってしまうのだ。 このような「表記揺れ」にはアプリケーション側である程度うまく対応することも可能だ。たとえば弊社のデモサイトで示しているように、「松下電器」で検索したときは、「パナソニック」を含むドキュメントも検索できるが、「松下電器」のドキュメントの方を「パナソニック」よりも上位にランキングされるようにする、という調整ができる。しかし、これは自分で作成したアプリケーションの場合である。nabbleやAtlassian JIRAで"solrjs"を検索するにはどうすればよいだろう。 こんなときは、"solrjs*"と、単語の末尾にワイルドカード検索の記号の"*"(アスタリスク)をつけてやるとよい: http://www.nabble.com/forum/Search.jtp?forum=14479&local=y&query=solrjs* すると今度は"solrj"が含まれずに"solrjs"だけが検索結果に含まれるようになる。理由は、LuceneのQueryParserはワイルドカード検索の単語を見つけると、AnalyzerをすっとばしてただちにWildcardQueryを作成するという仕様になっているためである。 このことを知らずに"solrjs -solrj"などと頑張ってやってみてもだめだ。これだとAnalyzerを通ったあとに"solrj NOT solrj"というBooleanQueryになってしまうからだ。 2009.07.28 Tuesday
Solr 1.4のREADME.txtに、ソフトウェア輸出管理規制に関する注意書き
Solr 1.4はTikaを含んでおり、TikaはPDFBoxを含んでおり、PDFBoxはCrypto APIのBouncyCastleを使用している。ゆえに、Apacheのルールに従い、Solr 1.4のREADME.txtに次の項目が記載されることとなった:
Export control ------------------------------------------------- This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this Apache Software Foundation distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code. The following provides more details on the included cryptographic software: Apache Solr uses the Apache Tika which uses the Bouncy Castle generic encryption libraries for extracting text content and metadata from encrypted PDF files. See http://www.bouncycastle.org/ for more details on Bouncy Castle. 現在、米国のテロ支援国家の指定を受けているのは、イラン・キューバ・シリア・スーダンであり、これに個人やグループを加えたクライアント向けに暗号化ソフトウェアなどを輸出することが禁じられている。しかしこれには例外があり、「ECCN 5D002によってコードがコントロールされており」、「コードが公開されており」、「BIS(U.S. Government's Bureau of Industry and Security)に連絡すること」を守れば輸出や再輸出が許可されるらしい。今回の措置はこれに倣った手続きの模様。BISへもメールで連絡がされた: https://issues.apache.org/jira/browse/SOLR-862 2009.07.14 Tuesday
LuSQL
コマンドラインからSQL文を指定してRDBのテーブルデータからLuceneのインデックスを高速に作成するツール:
http://code4lib.org/files/glen_newton_LuSql.pdf マルチスレッドで動作し、SolrのDIHよりも4〜10倍高速という比較結果だが、設定の柔軟性はDIHの方がいろいろできるので目的に応じて使い分けるといいだろう。 LuSQLのコマンドラインで設定できる項目は次の通り:
実際のコマンドラインは次のようになる(上記のPDFスライドから拝借): 2009.07.11 Saturday
FieldにTokenStreamを設定できるメソッドの追加(2.9)
Field.setTokenStream(TokenStream)というメソッドが追加された:
http://issues.apache.org/jira/browse/LUCENE-1699 これにより、storedフィールド値とは無関係な単語をフィールドにインデクシングできるようになる。SolrではUpdateProcessorにてあるフィールドで生成したTokenStreamを別のフィールドのTokenStreamとして使いたいときにこのメソッドが使えそうだ。 2009.07.09 Thursday
新しいハイライタ(2.9)
TermFreqVectorを使用した新しいハイライタが追加された:
https://issues.apache.org/jira/browse/LUCENE-1522 次のような特徴を持つ:
現在ひとつ制限事項があり、マルチバリュー(Documentの同じField名に複数回のadd(Field)を行うこと)のフィールドのオフセット値が正しくなくなる不具合のために、マルチバリューのフィールドでは正しく働かない。この不具合改修はLucene 2.9に入るかどうか、微妙なところである: https://issues.apache.org/jira/browse/LUCENE-1448 2009.07.08 Wednesday
Document.getFields()で返されるフィールドの順番の不具合の修正(2.9)
Lucene 2.3まではDocumentにadd()した(名前の異なる)Fieldの順番は、IndexReader/IndexSearcherで取得したDocumentに対してfields()/getFields()を実行して取得したときにも順番が保たれていた。ところが、LUCENE-1301の機能改善の時にこのルールが破られてしまった。現在、Lucene 2.9でこれを元に戻そうとしている:
https://issues.apache.org/jira/browse/LUCENE-1727 したがって、狭間のLucene 2.4ではDocumentに対してfields()/getFields()を実行して取得したFieldのリストは保存時の順番とは異なってしまう不具合が残ったままである。 なお、マルチバリュー(multi valued;Documentに対して同じフィールド名で2つ以上のフィールドをadd()すること)の順序はいずれのバージョンでも保たれている。 2009.07.07 Tuesday
NumericRangeQuery, NumericField, NumericTokenStream (2.9)
Lucene 2.9よりRangeQuery/RangeFilterがdeprecatedとなり、それぞれTermRangeQuery/TermRangeFilterと名前を変えた。TermRangeQueryはデフォルトで従来のConstantScoreRangeQueryにリライトされる:
https://issues.apache.org/jira/browse/LUCENE-1713 これは2.9になってから当初org.apache.lucene.search.trieパッケージに提供されたTrieRangeQuery(旧称)がNumericRangeQueryと名前を変えてコアのorg.apache.lucene.searchパッケージに移されたことに伴う名称変更である。 TrieRangeQueryは下図のようなトライ木のノードをインデクシングすることで範囲検索を速くするLucene 2.9の目玉機能である。 上図はTrieRangeQuery(contrib当時のオリジナルの名前)の提供者であるUwe Schindler氏の名前で検索するとヒットする同氏の論文から拝借したものである。従来のConstantScoreRangeQueryによる範囲検索では、(上図の葉の部分である)423から642の範囲を検索しようとした場合、TermEnumにより423から642までのTermをすべて(423,445,446,...,642)アクセスするためにI/Oが多数発生してしまっていた。TrieRangeQueryではインデックス中に上図のトライ木を展開し、範囲検索時には太線のノードのみをアクセスすることでI/Oの回数を減らして範囲検索を速くしている。 TrieRangeQueryは今ではNumericRangeQueryと名前を変えている。NumericRangeQueryの使い方は次のようである: public class TestNumericRangeQuery { static Directory dir = new RAMDirectory(); static final String RF = "range"; //static final String RFS = "rangeStore"; static final String FF = "flag"; static Analyzer analyzer = new WhitespaceAnalyzer(); static final int NUM = 10; static final int[] rangeValues = { 1, 2, 3, 4, 5, 6, 7, 8, 20, 30 }; static final String[] flagValues = { "0", "1", "1", "0", "1", "0", "1", "0", "1", "1" }; static final int precisionStep = 4; public static void main(String[] args) throws Exception { makeIndex(); searchRange( 2, 8 ); } static void makeIndex() throws Exception { IndexWriter writer = new IndexWriter( dir, analyzer, true, MaxFieldLength.LIMITED ); for( int i = 0; i < NUM; i++ ){ Document doc = new Document(); doc.add( new NumericField( RF, precisionStep, Store.YES, true ).setIntValue( rangeValues[i] ) ); doc.add( new Field( FF, flagValues[i], Store.YES, Index.ANALYZED ) ); writer.addDocument( doc ); } writer.close(); } static void searchRange( int begin, int end ) throws Exception { IndexSearcher searcher = new IndexSearcher( dir ); Query query = getQuery( getRangeQuery( begin, end ) ); TopDocs docs = searcher.search( query, 10 ); for( ScoreDoc scoreDoc : docs.scoreDocs ){ int d = scoreDoc.doc; float score = scoreDoc.score; System.out.println( score + " :¥t" + getDoc( searcher, d ) ); } searcher.close(); } static Query getRangeQuery( int begin, int end ){ Query query = NumericRangeQuery.newIntRange( RF, precisionStep, begin, end, true, true ); return query; } static Query getQuery( Query rangeQuery ){ BooleanQuery query = new BooleanQuery(); query.add( rangeQuery, Occur.MUST ); query.add( new TermQuery( new Term( FF, "1" ) ), Occur.MUST ); return query; } static String getDoc( IndexSearcher searcher, int d ) throws Exception { Document doc = searcher.doc( d ); return RF + "=" + doc.get( RF ) + ", " + FF + "=" + doc.get( FF ); } } インデックスを作成するときにはNumericFieldを使うが、大量のドキュメントを生成する際はトライ木を生成するためのコストがかかってしまう。そのようなときは、NumericTokenStreamを使って次のようにFieldオブジェクトを再利用するとよい: static void makeIndex() throws Exception { IndexWriter writer = new IndexWriter( dir, analyzer, true, MaxFieldLength.LIMITED ); NumericTokenStream stream = new NumericTokenStream( precisionStep ); Field field = new Field( RF, stream ); field.setOmitNorms( true ); field.setOmitTermFreqAndPositions( true ); Document doc = new Document(); doc.add( field ); for( int i = 0; i < NUM; i++ ){ stream.setIntValue( rangeValues[i] ); doc.removeField( FF ); doc.add( new Field( FF, flagValues[i], Store.YES, Index.ANALYZED ) ); writer.addDocument( doc ); } writer.close(); } ただし、NumericTokenStreamを使ったときは、当該FieldはStore.YESに設定することはできない。 2009.07.01 Wednesday
(書籍)Search User Interface
検索エンジンのアルゴリズムではなく、検索システムのユーザインタフェースに着目した本。Webでただで読めます:
http://searchuserinterfaces.com/book/
|
+ Solrによるブログ内検索
+ PROFILE
+ LINKS
+ Lucene&Solrデモ
+ ThinkIT記事
+ RECOMMEND
+ RECOMMEND
Lucene in Action (JUGEMレビュー »)
Erik Hatcher,Otis Gospodnetic,Mike McCandless FastVectorHighlighterについて解説記事を寄稿しました。
+ RECOMMEND
+ SELECTED ENTRIES
+ RECENT COMMENTS
+ RECENT TRACKBACK
+ CATEGORIES
+ ARCHIVES
+ MOBILE
+ SPONSORED LINKS
|
(C) 2024 ブログ JUGEM Some Rights Reserved.
|
PAGE TOP |