関口宏司のLuceneブログ

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

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

| スポンサードリンク | - | | - | - |
ようやく Lucene 2.9.0 がリリース
本日、Lucene 2.9.0がリリースされた。本リリースでは残念ながら一部APIの後方互換性を損なう変更が含まれている:

Changes in backwards compatibility policy

これまでのLuceneアプリケーションでLucene 2.9.0を使おうとする場合は、まず、Lucene 2.9.0を入手してアプリケーションをコンパイルし直す必要がある。そこでコンパイルエラーを発見したなら互換性の問題に触れている可能性があるので、2.9のJavadocを参照しながらアプリケーションを修正しなければならない。

2.9には多数の不具合修正の他、次のような新機能が追加されている:



次に予定されているリリース3.0は、2.9でdeprecatedとマークされているAPIが削除され、Java 1.5以上が必要なリリースとなる。なお、Lucene in Action, 2nd EditionはLucene 3.0に対応した内容となる予定である。
| 関口宏司 | Luceneリリース | 12:06 | comments(2) | trackbacks(0) |
Lucene Contrib コミッターに・・・
なりました。

http://lucene.apache.org/java/docs/whoweare.html
| 関口宏司 | その他(分類不能) | 07:24 | comments(4) | trackbacks(0) |
短いフィールドにはコンスタントなtfが有効な件
タイトルのような短いフィールドには、tfを考慮しない方がいい場合がある。たとえば、"New York"というタイトルのDVDを探しているとき、DefaultSimilarityのtf()が効いていると、ずばり"New York"というタイトルよりも"New York, New York"の方がスコアが高くなってしまう:

New York, New York : 0.5945349
0.5945349 = (MATCH) sum of:
  0.29726744 = (MATCH) weight(title:new in 0), product of:
    0.70710677 = queryWeight(title:new), product of:
      0.5945349 = idf(docFreq=2, maxDocs=2)
      1.1893445 = queryNorm
    0.42039964 = (MATCH) fieldWeight(title:new in 0), product of:
      1.4142135 = tf(termFreq(title:new)=2)
      0.5945349 = idf(docFreq=2, maxDocs=2)
      0.5 = fieldNorm(field=title, doc=0)
  0.29726744 = (MATCH) weight(title:york in 0), product of:
    0.70710677 = queryWeight(title:york), product of:
      0.5945349 = idf(docFreq=2, maxDocs=2)
      1.1893445 = queryNorm
    0.42039964 = (MATCH) fieldWeight(title:york in 0), product of:
      1.4142135 = tf(termFreq(title:york)=2)
      0.5945349 = idf(docFreq=2, maxDocs=2)
      0.5 = fieldNorm(field=title, doc=0)

New York : 0.5254995
0.5254995 = (MATCH) sum of:
  0.26274976 = (MATCH) weight(title:new in 1), product of:
    0.70710677 = queryWeight(title:new), product of:
      0.5945349 = idf(docFreq=2, maxDocs=2)
      1.1893445 = queryNorm
    0.3715843 = (MATCH) fieldWeight(title:new in 1), product of:
      1.0 = tf(termFreq(title:new)=1)
      0.5945349 = idf(docFreq=2, maxDocs=2)
      0.625 = fieldNorm(field=title, doc=1)
  0.26274976 = (MATCH) weight(title:york in 1), product of:
    0.70710677 = queryWeight(title:york), product of:
      0.5945349 = idf(docFreq=2, maxDocs=2)
      1.1893445 = queryNorm
    0.3715843 = (MATCH) fieldWeight(title:york in 1), product of:
      1.0 = tf(termFreq(title:york)=1)
      0.5945349 = idf(docFreq=2, maxDocs=2)
      0.625 = fieldNorm(field=title, doc=1)


このようなときはDefaultSimilarityのtf()をオーバライドして常に1が返るようにする。すると:

New York : 0.5254995
0.5254995 = (MATCH) sum of:
  0.26274976 = (MATCH) weight(title:new in 1), product of:
    0.70710677 = queryWeight(title:new), product of:
      0.5945349 = idf(docFreq=2, maxDocs=2)
      1.1893445 = queryNorm
    0.3715843 = (MATCH) fieldWeight(title:new in 1), product of:
      1.0 = tf(termFreq(title:new)=1)
      0.5945349 = idf(docFreq=2, maxDocs=2)
      0.625 = fieldNorm(field=title, doc=1)
  0.26274976 = (MATCH) weight(title:york in 1), product of:
    0.70710677 = queryWeight(title:york), product of:
      0.5945349 = idf(docFreq=2, maxDocs=2)
      1.1893445 = queryNorm
    0.3715843 = (MATCH) fieldWeight(title:york in 1), product of:
      1.0 = tf(termFreq(title:york)=1)
      0.5945349 = idf(docFreq=2, maxDocs=2)
      0.625 = fieldNorm(field=title, doc=1)

New York, New York : 0.42039964
0.42039964 = (MATCH) sum of:
  0.21019982 = (MATCH) weight(title:new in 0), product of:
    0.70710677 = queryWeight(title:new), product of:
      0.5945349 = idf(docFreq=2, maxDocs=2)
      1.1893445 = queryNorm
    0.29726744 = (MATCH) fieldWeight(title:new in 0), product of:
      1.0 = tf(termFreq(title:new)=2)
      0.5945349 = idf(docFreq=2, maxDocs=2)
      0.5 = fieldNorm(field=title, doc=0)
  0.21019982 = (MATCH) weight(title:york in 0), product of:
    0.70710677 = queryWeight(title:york), product of:
      0.5945349 = idf(docFreq=2, maxDocs=2)
      1.1893445 = queryNorm
    0.29726744 = (MATCH) fieldWeight(title:york in 0), product of:
      1.0 = tf(termFreq(title:york)=2)
      0.5945349 = idf(docFreq=2, maxDocs=2)
      0.5 = fieldNorm(field=title, doc=0)


サンプルプログラムは次の通り。searchIndex()メソッド内のコメントを有効/無効にすると切り替えられる:

public class TestNewYork {

  static final String[] TITLES = { "New York, New York", "New York" };
  static final String F = "title";
  static Directory dir = new RAMDirectory();
  static Analyzer analyzer = new StandardAnalyzer( Version.LUCENE_CURRENT );
  
  public static void main(String[] args) throws Exception {
    makeIndex();
    searchIndex();
  }

  static void makeIndex() throws IOException {
    IndexWriter writer = new IndexWriter( dir, analyzer, true, MaxFieldLength.LIMITED );
    for( String title : TITLES ){
      Document doc = new Document();
      doc.add( new Field( F, title, Store.YES, Index.ANALYZED ) );
      writer.addDocument( doc );
    }
    writer.close();
  }
  
  static void searchIndex() throws Exception {
    QueryParser parser = new QueryParser( F, analyzer );
    Query query = parser.parse( "new york" );
    IndexSearcher searcher = new IndexSearcher( dir, true );
    //searcher.setSimilarity( new ConstantTfSimilarity() );
    TopDocs docs = searcher.search( query, 10 );
    for( ScoreDoc scoreDoc : docs.scoreDocs ){
      Document doc = searcher.doc( scoreDoc.doc );
      float score = scoreDoc.score;
      System.out.println( doc.get( F ) + " : " + score );
      Explanation e = searcher.explain( query, scoreDoc.doc );
      System.out.println( e.toString() );
    }
    searcher.close();
  }
  
  static class ConstantTfSimilarity extends DefaultSimilarity {
    public float tf( float freq ){
      return 1;
    }
  }
}
| 関口宏司 | Luceneスコアリング | 11:50 | comments(0) | trackbacks(0) |
(メモ)GAELucene - Lucene runs on Google AppEngine!
http://www.nabble.com/Run-your-Lucene-Applications-on-Google-AppEngine-with-GAELucene-td25438615.html
| 関口宏司 | Luceneツール | 01:44 | comments(0) | trackbacks(0) |
並列インデックスを利用したDocumentの更新
Luceneのインデックスに登録されている既存Documentを更新する場合、LuceneではDocumentをいったん削除し、再度新規にDocument全体を追加しなければならない。このため、サイズの小さなフィールドの追加や内容変更をする場合でも、変更の必要がないサイズの大きなフィールドにまでその影響がおよぼされてしまう。そのため、SolrなどでもStoredデータを使ってフィールドの更新をできるようにしようという提案もされたりしている:

https://issues.apache.org/jira/browse/SOLR-139

このようなフィールドのちょっとした追加機能に関しては以前から要望が多かったが、Luceneの将来バージョンで機能制限付きながらも「並列インデックス(Parallel Index)」を利用したDocumentの更新をできるようにしよう、という提案がなされている:

https://issues.apache.org/jira/browse/LUCENE-1879
http://wiki.apache.org/lucene-java/ParallelIncrementalIndexing

「並列インデックス」とは、異なるフィールドを保持している複数のインデックスでかつそれぞれのインデックスで同一Documentを特定できるようにDocument IDが割り振られているインデックスを指す。ひとつのDocumentをフィールド別に複数のインデックスに分けて登録する場合、そのDocumentが同じIDを割り振られるようにするには、インデックスごとに同じ順序でDocumentを追加していく必要がある。

このような「並列インデックス」をオープンして検索に使用するために、ParallelReaderというIndexReaderがある。並列インデックスを作成し、ParallelReaderを使ってインデックスの内容を表示するサンプルプログラムを以下に示す:
public class TestParallelReader {
  
  static final String[] BODIES = {
    "This is body 0", "This is body 1", "This is body 2"
  };
  static final String F_BODY = "body";
  static final String[] PRICES = { "100", "110", "120" };
  static final String F_PRICE = "price";
  static final String[] AUTHORS = { "Erik", "Yonik", "Mike" };
  static final String F_AUTHOR = "author";
  static Analyzer analyzer = new WhitespaceAnalyzer();

  public static void main(String[] args) throws Exception {
    ParallelReader pr = new ParallelReader( true );

    Directory bodiesDir = makeIndex( F_BODY, BODIES );
    IndexReader bodiesReader = IndexReader.open( bodiesDir, true );
    pr.add( bodiesReader );
    printAllContents( pr );

    Directory pricesDir = makeIndex( F_PRICE, PRICES );
    IndexReader pricesReader = IndexReader.open( pricesDir, true );
    pr.add( pricesReader );
    printAllContents( pr );

    Directory authorsDir = makeIndex( F_AUTHOR, AUTHORS );
    IndexReader authorsReader = IndexReader.open( authorsDir, true );
    pr.add( authorsReader );
    printAllContents( pr );

    pr.close();
  }

  static Directory makeIndex( String name, String[] contents ) throws Exception {
    System.out.println( "¥n===== " + name + " フィールドをインデックスに追加 =====" );
    Directory dir = new RAMDirectory();
    IndexWriter writer = new IndexWriter( dir, analyzer, true, MaxFieldLength.LIMITED );
    for( String content : contents ){
      Document doc = new Document();
      doc.add( new Field( name, content, Store.YES, Index.ANALYZED ) );
      writer.addDocument( doc );
    }
    writer.close();
    return dir;
  }
  
  static void printAllContents( IndexReader reader ) throws Exception {
    System.out.println( "===== インデックスの内容を表示 =====" );
    IndexSearcher searcher = new IndexSearcher( reader );
    Query query = new MatchAllDocsQuery();
    TopDocs docs = searcher.search( query, 10 );
    for( ScoreDoc scoreDoc : docs.scoreDocs ){
      Document doc = searcher.doc( scoreDoc.doc );
      for( Field field : (List)doc.getFields() ){
        System.out.print( field.name() + ":" + doc.get( field.name() ) + ", " );
      }
      System.out.println();  // line break
    }
    searcher.close();
  }
}

サンプルプログラムでは、インデックスに3つのフィールドを順次追加している。通常であれば既存のインデックスのDocumentの全体を削除して再登録しなければならないが、サンプルプログラムでは新しいフィールドを新しいインデックスに登録し、ParallelReaderを使ってopenしている。実行結果は次のようになる:
===== body フィールドをインデックスに追加 =====
===== インデックスの内容を表示 =====
body:This is body 0, 
body:This is body 1, 
body:This is body 2, 

===== price フィールドをインデックスに追加 =====
===== インデックスの内容を表示 =====
body:This is body 0, price:100, 
body:This is body 1, price:110, 
body:This is body 2, price:120, 

===== author フィールドをインデックスに追加 =====
===== インデックスの内容を表示 =====
body:This is body 0, price:100, author:Erik, 
body:This is body 1, price:110, author:Yonik, 
body:This is body 2, price:120, author:Mike, 

実際にこの方法を拡張してDocumentのフィールドを更新するにはいくつかの壁を乗り越える必要がある。たとえばDocument IDを複数にまたがったインデックスで同じ値に保つのは非常に難しい。Documentを削除すると、次のマージのタイミングでDocument IDはずれてしまう。現在のLuceneでは、マージはDocument数ではなくサイズ基準で発生するのがデフォルトになっているが、これではインデックスごとにマージ実行のタイミングがずれてしまうので、flush/delete/mergeのタイミングは同一に保つ必要がある。

そのためのアイディアとしては、複数のインデックスのそれぞれを担当するIndexWriterを、ひとつのマスターと複数のスレーブに分ける。そしてスレーブのIndexWriterは、マスターのIndexWriterのタイミングに合わせてマージを行ったらどうか、という提案の内容になっている。
| 関口宏司 | Luceneインデックス | 13:55 | comments(0) | trackbacks(0) |
+ Solrによるブログ内検索
+ PROFILE
  12345
6789101112
13141516171819
20212223242526
27282930   
<< September 2009 >>
+ 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