CLOVER🍀

That was when it all began.

InfinispanをApache LuceneのDirectoryとして使う

Infinispanのクエリをもうちょっと勉強してみようと思い、その時に合わせてLuceneも覚えられたらいいなぁということで。

で、現時点でのLuceneの最新版は4.3.0なのですが、
Apache Lucene
http://lucene.apache.org/core/index.html

Infinispanでクエリを使う時に使用するHibernate Searchは、Lucene 3.6までの対応となっています。
Hibernate Search
http://www.hibernate.org/subprojects/search.html

Hibernate Searchでも、Lucene 4系への対応はJIRAに挙がっているのですが
Update to Lucene 4.0
https://hibernate.atlassian.net/browse/HSEARCH-1191

対応する目標がHibernate Search 5.0になっていますし、今のGitHubでのmasterブランチは4.3.0のSNAPSHOTなので、これは当分対応しないだろうなぁと…。
https://github.com/hibernate/hibernate-search

というわけで、Hibernate Searchを介さずに、普通にLuceneのDirectoryとしてInfinispanを触ってみることにしました。

Infinispan as a storage for Lucene indexes
https://docs.jboss.org/author/display/ISPN/Infinispan+as+a+storage+for+Lucene+indexes

まずは、動作させてみて、その後ページの英訳に進んでいこうと思います。

*ドキュメントの英訳は、こちらへ
http://d.hatena.ne.jp/Kazuhira/20130519/1368965554

動かしてみる

InfinispanでLuceneのDirectoryを作成するには、org.infinispan.lucene.directory.DirectoryBuilderクラスを使います。

Cache cache = // create an Infinispan cache, configured as you like
Directory indexDir = DirectoryBuilder.newDirectoryInstance(cache, cache, cache, indexName)
                                     .create();

そして、Directoryの作成には、3つのCacheが必要になります。それぞれ意味があるのですが、InfinispanのWikiではサンプルとしては全て同じCacheを使用しています。厳密には、用途毎に分けた方がよいみたいです。この辺りは、後で…。
最後の引数は、インデックスの名前になります。インデックス毎に、ユニークな名前を指定する必要があります。

では、これを使ってLuceneのサンプルを書いてみます。
Introduction to Lucene's APIs
http://lucene.apache.org/core/4_3_0/core/overview-summary.html#overview_description

build.sbt

name := "infinispan-lucene"

version := "0.0.1-SNAPSHOT"

scalaVersion := "2.10.1"

organization := "littlewings"

libraryDependencies ++= Seq(
  "org.infinispan" % "infinispan-core" % "5.3.0.Beta2",
  "org.infinispan" % "infinispan-lucene-directory" % "5.3.0.Beta2",
  "org.apache.lucene" % "lucene-core" % "4.3.0",
  "org.apache.lucene" % "lucene-analyzers-common" % "4.3.0",
  "org.apache.lucene" % "lucene-queryparser" % "4.3.0"
)

Infinispanは、5.3からLucene 4に対応しています。が、5.3はまだ開発中なのでBeta2ですね…。今回、これで初めてBeta版を触っています…。
また、Infinispan 5.3はLucene 3系にも4系にも対応していますが、デフォルトは3系を見ているので、Lucene 4への依存関係を定義すると、Lucene 3の依存関係がevictされてLucene 4用のDirectoryを使うようになります。

サンプルコード。
src/main/scala/InfinispanLucene.scala

import org.infinispan.Cache
import org.infinispan.lucene.directory.DirectoryBuilder
import org.infinispan.manager.DefaultCacheManager

import org.apache.lucene.analysis.standard.StandardAnalyzer
import org.apache.lucene.document.{Document, Field, TextField}
import org.apache.lucene.index.{DirectoryReader, IndexWriter, IndexWriterConfig}
import org.apache.lucene.queryparser.classic.QueryParser
import org.apache.lucene.search.IndexSearcher
import org.apache.lucene.store.Directory
import org.apache.lucene.util.Version

object InfinispanLucene {
  def main(args: Array[String]): Unit = {
    val manager = new DefaultCacheManager
    // 各種キャッシュの作成
    val metaDataCache: Cache[_, _] = manager.getCache("metaDataCache")
    val chunksCache: Cache[_, _] = manager.getCache("chunksCache")
    val distLocksCache: Cache[_, _] = manager.getCache("distLocksCache")

    val luceneVersion = Version.LUCENE_43

    // LuceneのDirectoryを作成
    val indexDir =
      DirectoryBuilder
        .newDirectoryInstance(metaDataCache, chunksCache, distLocksCache, "indexName")
        .create()

    // 今回使用するAnalyzer
    val analyzer = new StandardAnalyzer(luceneVersion)

    // インデックスへの登録
    val indexWriterConfig = new IndexWriterConfig(luceneVersion, analyzer)
    val indexWriter = new IndexWriter(indexDir, indexWriterConfig)

    val document = new Document
    val text = "This is the text to be indexed."
    document.add(new Field("fieldName", text, TextField.TYPE_STORED))
    
    indexWriter.addDocument(document)
    indexWriter.close()

    // インデックスからの読み出し
    val directoryReader = DirectoryReader.open(indexDir)
    val indexSearcher = new IndexSearcher(directoryReader)

    // Queryの作成
    val queryParser = new QueryParser(luceneVersion, "fieldName", analyzer)
    val query = queryParser.parse("text")
    val hits = indexSearcher.search(query, null, 1000).scoreDocs

    // 検索結果表示
    println(s"hits length => ${hits.length}")

    for (h <- hits) {
      val hitDoc = indexSearcher.doc(h.doc)
      println(s"Hit Text => ${hitDoc.get("fieldName")}")
    }

    directoryReader.close()

    indexDir.close()

    metaDataCache.stop()
    chunksCache.stop()
    distLocksCache.stop()
    manager.stop()
  }
}

Infinispanを使っていること以外は、ほぼLuceneのサンプルのままです。

一応、Cacheは別々に用意しました。

    // 各種キャッシュの作成
    val metaDataCache: Cache[_, _] = manager.getCache("metaDataCache")
    val chunksCache: Cache[_, _] = manager.getCache("chunksCache")
    val distLocksCache: Cache[_, _] = manager.getCache("distLocksCache")

    // ...

    // LuceneのDirectoryを作成
    val indexDir =
      DirectoryBuilder
        .newDirectoryInstance(metaDataCache, chunksCache, distLocksCache, "indexName")
        .create()

これとCache、DefaultCacheManagerのstop以外は、ほぼサンプルと同じです。ちょっと変数名が変わっていたり、Scalaでの書き方になっていたりしますが。

動かしてみます。

> run
[info] Running InfinispanLucene 
5 19, 2013 2:08:48 午後 org.infinispan.factories.GlobalComponentRegistry start
INFO: ISPN000128: Infinispan version: Infinispan 'Tactical Nuclear Penguin' 5.3.0.Beta2
5 19, 2013 2:08:49 午後 org.infinispan.jmx.CacheJmxRegistration start
INFO: ISPN000031: MBeans were successfully registered to the platform MBean server.
5 19, 2013 2:08:49 午後 org.infinispan.jmx.CacheJmxRegistration start
INFO: ISPN000031: MBeans were successfully registered to the platform MBean server.
5 19, 2013 2:08:49 午後 org.infinispan.jmx.CacheJmxRegistration start
INFO: ISPN000031: MBeans were successfully registered to the platform MBean server.
hits length => 1
Hit Text => This is the text to be indexed.
[success] Total time: 4 s, completed 2013/05/19 14:08:51

動きましたねー。

このサンプルだけだと、LuceneのRAMDirectoryを使った場合と、違いがある気がしませんね…。

ここからページの英訳をしようと思ったのですが、エントリが長くなりそうなので別にします。