読者です 読者をやめる 読者になる 読者になる

Apache Solr を組み込み実行

Java Groovy

オープンソース全文検索エンジン Apache Solr は、 Servlet として実装されており、通常は jetty 等のサーブレットエンジン(コンテナ)で実行しますが、今回は組み込み実行を試してみました。

ソースは http://github.com/fits/try_samples/tree/master/blog/20140710/

はじめに

Solr を組み込み実行 (サーバーを起動せずに直接処理を実行) するには下記のような方法が考えられます。

  • (1) EmbeddedSolrServer を使用
  • (2) SolrCore を使用

(1) は SolrJ の API を使うので高レベル API、(2) は Core API をそのまま使うので低レベル API といったところでしょうか。

(1) EmbeddedSolrServer を使用

EmbeddedSolrServer を使って検索処理を実装するのは簡単です。

今回は、実行時引数 (第1引数で Solr のホームディレクトリ、第2引数でコア名) で指定したコアのドキュメントを全件取得するようにしてみました。

search1.groovy
@Grab('org.apache.solr:solr-core:4.9.0')
@Grab('org.slf4j:slf4j-nop:1.7.7')
import org.apache.solr.core.CoreContainer
import org.apache.solr.client.solrj.SolrQuery
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer

def cores = new CoreContainer(args[0])
cores.load()

def server = new EmbeddedSolrServer(cores, args[1])
// 全件取得のクエリ
def q = new SolrQuery(query: '*:*')
// 検索
def result = server.query(q)
// 結果の出力
result.results.each {
    println it
}

server.shutdown()

なお、EmbeddedSolrServer をインスタンス化している箇所を new HttpSolrServer('http://localhost:8983/solr/collection1') のように書き換えれば、サーブレットエンジン上で実行している Solr サーバーに対して検索を行うようになります。

実行結果は下記の通りです。

実行結果
> groovy search1.groovy /solr-4.9.0/example/solr collection1

[id:GB18030TEST, name:Test with some GB18030 encoded characters, features:[No accents here, ?是一个功能, This is a feature (translated), ??文件是很有光?, This document is very shiny (translated)], price:0.0, price_c:0,USD, inStock:true, _version_:1473119790218346496]
・・・

(2) SolrCore を使用

次に、同様の処理を SolrCore を使って実装してみました。

SolrCore では XXXResponseWriter (BinaryResponseWriterJSONResponseWriter 等) を使って処理結果をストリームへ出力します。

search2.groovy
@Grab('org.apache.solr:solr-core:4.9.0')
@Grab('org.slf4j:slf4j-nop:1.7.7')
import org.apache.solr.core.CoreContainer
import org.apache.solr.request.LocalSolrQueryRequest
import org.apache.solr.response.SolrQueryResponse
import org.apache.solr.response.BinaryResponseWriter
import org.apache.solr.common.util.JavaBinCodec

def cores = new CoreContainer(args[0])
cores.load()

def core = cores.getCore(args[1])
// 検索ハンドラ取得
def handler = core.getRequestHandler('/query')

def req = new LocalSolrQueryRequest(core, [
    q: ['*:*'] as String[] // 全件取得
])
def res = new SolrQueryResponse()
// 検索
core.execute(handler, req, res)

def writer = new ByteArrayOutputStream()

def rw = new BinaryResponseWriter()
// 検索結果をストリームへ出力
rw.write(writer, req, res)

def resolver = new BinaryResponseWriter.Resolver(req, res.getReturnFields())
def bais = new ByteArrayInputStream(writer.toByteArray())
// バイト配列を Java オブジェクト化 (ここでは SimpleOrderedMap オブジェクトが返ります)
def result = new JavaBinCodec(resolver).unmarshal(bais)
// 結果の出力
result.response.each {
    println it
}

core.close()
cores.shutdown()

Solr に含まれている example/solrcollection1 コアを使う場合は、/query の他に /select ハンドラも利用でき、違いはデフォルト設定値だけのようです。 (/query は indent が true に設定されている等)

実行結果は下記の通りです。

実行結果
> groovy search2.groovy /solr-4.9.0/example/solr collection1

[id:GB18030TEST, name:Test with some GB18030 encoded characters, features:[No accents here, ?是一个功能, This is a feature (translated), ??文件是很有光?, This document is very shiny (translated)], price:0.0, price_c:0,USD, inStock:true, _version_:1473119790218346496]
・・・

JSON で結果出力

JSON で検索結果を出力したい場合、BinaryResponseWriter の代わりに JSONResponseWriter を使用します。

search2_json.groovy
・・・
// 検索
core.execute(handler, req, res)

def writer = new StringWriter()

def rw = new JSONResponseWriter()
rw.write(writer, req, res)
// 結果の出力 (JSON)
println writer.toString()
・・・
実行結果
> groovy search2_json.groovy /solr-4.9.0/example/solr collection1

{
  "responseHeader":{
    "status":0,
    "QTime":94,
    "params":{
      "q":"*:*"}},
  "response":{"numFound":32,"start":0,"docs":[
      {
        "id":"GB18030TEST",
        "name":"Test with some GB18030 encoded characters",
        "features":["No accents here",
          "?是一个功能",
          "This is a feature (translated)",
          "??文件是很有光?",
          "This document is very shiny (translated)"],
        "price":0.0,
        "price_c":"0,USD",
        "inStock":true,
        "_version_":1473119790218346496},
  ・・・