Lucene API で Solr と Elasticsearch のインデックスを確認
Groovy で Lucene の API を使用して Solr や Elasticsearch のインデックスの内容を確認してみました。(Lucene 6.2.1 の API を使用)
ソースは http://github.com/fits/try_samples/tree/master/blog/20161024/
(a) ドキュメントの内容を出力
まずは、ドキュメントに属するフィールドの内容を出力する処理です。
DirectoryReader
から Document
を取得し、フィールド IndexableField
の内容を出力しています。
dump_docs.groovy
@Grab('org.apache.lucene:lucene-core:6.2.1') import org.apache.lucene.index.DirectoryReader import org.apache.lucene.store.FSDirectory import java.nio.file.Paths def dir = FSDirectory.open(Paths.get(args[0])) DirectoryReader.open(dir).withCloseable { reader -> println "numDocs = ${reader.numDocs()}" (0..<reader.numDocs()).each { // ドキュメントの取得 def doc = reader.document(it) println "---------- doc: ${it} ----------" // ドキュメント内のフィールドを出力 doc.fields.each { f -> def value = f.binaryValue()? f.binaryValue().utf8ToString(): f.stringValue() println "<field> name=${f.name}, value=${value}, class=${f.class}" } } }
(b) Term の内容を出力
インデックス内のフィールド情報と Term を出力する処理です。 Term は基本的な検索の単位となっており、Term の内容を見れば単語の分割状況を確認できます。
これらの情報を取得するには LeafReader
を使います。
Term の内容 BytesRef
は TermsEnum
から取得できます。
LeafReader から terms
メソッドで該当フィールドの Terms
を取得し、iterator
メソッドで TermsEnum を取得します。
dump_terms.groovy
@Grab('org.apache.lucene:lucene-core:6.2.1') import org.apache.lucene.index.DirectoryReader import org.apache.lucene.store.FSDirectory import java.nio.file.Paths def dir = FSDirectory.open(Paths.get(args[0])) DirectoryReader.open(dir).withCloseable { reader -> reader.leaves().each { ctx -> // LeafReader の取得 def leafReader = ctx.reader() println "---------- leaf: ${leafReader} ----------" // フィールド情報の出力 leafReader.getFieldInfos().each { fi -> println "<fieldInfo> name: ${fi.name}, valueType: ${fi.docValuesType}, indexOptions: ${fi.indexOptions}" } leafReader.fields().each { name -> // 指定のフィールド名に対する TermsEnum を取得 def termsEnum = leafReader.terms(name).iterator() println '' println "===== <term> name=${name} =====" try { while(termsEnum.next() != null) { // Term の内容を出力 println "term=${termsEnum.term().utf8ToString()}, freq=${termsEnum.docFreq()}" } } catch(e) { } } } }
動作確認
Lucene のバージョンが以下のようになっていたので、今回は Solr 6.2.1 と Elasticsearch 5.0.0 RC1 のインデックス内容を確認してみます。
プロダクト | 使用している Lucene のバージョン |
---|---|
Solr 6.2.1 | Lucene 6.2.1 |
Elasticsearch 2.4.1 | Lucene 5.5.2 |
Elasticsearch 5.0.0 RC1 | Lucene 6.2.0 |
(1) Solr 6.2.1
Solr 6.2.1 のインデックスから確認してみます。
準備
インデックスを作成してドキュメントを登録しておきます。
1. Solr 起動とインデックスの作成
> solr start ・・・ > solr create -c sample { "responseHeader":{ "status":0, "QTime":9197}, "core":"sample"}
2. スキーマの登録
schema.json
{ "add-field": { "name": "title", "type": "string" }, "add-field": { "name": "num", "type": "int" }, "add-field": { "name": "rdate", "type": "date" } }
スキーマ登録
$ curl -s http://localhost:8983/solr/sample/schema --data-binary @schema.json { "responseHeader":{ "status":0, "QTime":554}}
3. ドキュメントの登録
data1.json
{ "title": "item1", "num": 11, "rdate": "2016-10-20T13:45:00Z" }
ドキュメント登録
$ curl -s http://localhost:8983/solr/sample/update/json/docs --data-binary @data1.json {"responseHeader":{"status":0,"QTime":199}}
ちなみに、コミットしなくてもインデックスファイルには反映されるようです。
インデックスの内容確認
それでは、インデックスの内容を確認します。
該当するインデックスのディレクトリ(例. C:\solr-6.2.1\server\solr\sample\data\index
)を引数に指定して実行します。
(a) ドキュメントの内容
> groovy dump_docs.groovy C:\solr-6.2.1\server\solr\sample\data\index numDocs = 1 ---------- doc: 0 ---------- <field> name=title, value=item1, class=class org.apache.lucene.document.StoredField <field> name=num, value=11, class=class org.apache.lucene.document.StoredField <field> name=rdate, value=1476971100000, class=class org.apache.lucene.document.StoredField <field> name=id, value=2b1080dd-0cd3-43c6-a3ff-ab618ad00113, class=class org.apache.lucene.document.StoredField
(b) Term の内容
> groovy dump_terms.groovy C:\solr-6.2.1\server\solr\sample\data\index ---------- leaf: _0(6.2.1):C1 ---------- <fieldInfo> name: title, valueType: SORTED, indexOptions: DOCS <fieldInfo> name: _text_, valueType: NONE, indexOptions: DOCS_AND_FREQS_AND_POSITIONS <fieldInfo> name: num, valueType: NUMERIC, indexOptions: DOCS <fieldInfo> name: rdate, valueType: NUMERIC, indexOptions: DOCS <fieldInfo> name: id, valueType: SORTED, indexOptions: DOCS <fieldInfo> name: _version_, valueType: NUMERIC, indexOptions: DOCS ===== <term> name=_text_ ===== term=00, freq=1 term=0cd3, freq=1 term=11, freq=1 term=13, freq=1 term=1548710288432300032, freq=1 term=20, freq=1 term=2016, freq=1 term=2b1080dd, freq=1 term=43c6, freq=1 term=45, freq=1 term=a3ff, freq=1 term=ab618ad00113, freq=1 term=item1, freq=1 term=oct, freq=1 term=thu, freq=1 term=utc, freq=1 ===== <term> name=_version_ ===== term= ?yTP , freq=1 ===== <term> name=id ===== term=2b1080dd-0cd3-43c6-a3ff-ab618ad00113, freq=1 ===== <term> name=num ===== term= , freq=1 ===== <term> name=rdate ===== term= *~Yn`, freq=1 ===== <term> name=title ===== term=item1, freq=1
version・num・rdate の値が文字化けしているように見えますが、これは org.apache.lucene.util.LegacyNumericUtils.intToPrefixCoded()
メソッド等で処理されてバイナリデータとなっているためです。
実際の値を復元するには LegacyNumericUtils.prefixCodedToInt()
等を適用する必要があるようです。
なお、LegacyNumericUtils クラスは Lucene 6.2.1 API で deprecated となっていますが、Solr は未だ使っているようです。
(2) Elasticsearch 5.0.0 RC1
次は Elasticsearch です。
準備
インデックスを作成してドキュメントを登録しておきます。
1. Elasticsearch 起動
> elasticsearch ・・・
2. インデックスの作成とスキーマ登録
schema.json
{ "mappings": { "data": { "properties": { "title": { "type": "string", "index": "not_analyzed" }, "num": { "type": "integer" }, "rdate": { "type": "date" } } } } }
インデックス作成とスキーマ登録
$ curl -s -XPUT http://localhost:9200/sample --data-binary @schema.json {"acknowledged":true,"shards_acknowledged":true}
3. ドキュメントの登録
data1.json
{ "title": "item1", "num": 11, "rdate": "2016-10-20T13:45:00Z" }
ドキュメント登録
$ curl -s http://localhost:9200/sample/data --data-binary @data1.json {"_index":"sample","_type":"data","_id":"AVfwTjEQFnFWQdd5V9p5","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"created":true}
なお、すぐにインデックスファイルへ反映されない場合は flush を実施します。
flush 例
$ curl -s http://localhost:9200/sample/_flush
インデックスの内容確認
インデックスの内容を確認します。
Elasticsearch の場合はデフォルトで複数の shard に分かれているため、ドキュメントを登録した shard を確認しておきます。
shard の確認
$ curl -s http://localhost:9200/_cat/shards/sample?v index shard prirep state docs store ip node sample 1 p STARTED 0 130b 127.0.0.1 iUp_FE_ ・・・ sample 4 p STARTED 1 3.8kb 127.0.0.1 iUp_FE_ sample 4 r UNASSIGNED sample 0 p STARTED 0 130b 127.0.0.1 iUp_FE_ sample 0 r UNASSIGNED
shard 4 にドキュメントが登録されています。
Elasticsearch のインデックスディレクトリは data/nodes/<ノード番号>/indices/<インデックスのuuid>/<shard番号>/index
となっているようで、今回は data\nodes\0\indices\QBXMjcCFSWy26Gow1Y9ItQ\4\index
でした。(インデックスの uuid は QBXMjcCFSWy26Gow1Y9ItQ)
(a) ドキュメントの内容
> groovy dump_docs.groovy C:\elasticsearch-5.0.0-rc1\data\nodes\0\indices\QBXMjcCFSWy26Gow1Y9ItQ\4\index numDocs = 1 ---------- doc: 0 ---------- <field> name=_source, value={ "title": "item1", "num": 11, "rdate": "2016-10-20T13:45:00Z" } , class=class org.apache.lucene.document.StoredField <field> name=_uid, value=data#AVfwTjEQFnFWQdd5V9p5, class=class org.apache.lucene.document.StoredField
(b) Term の内容
> groovy dump_terms.groovy C:\elasticsearch-5.0.0-rc1\data\nodes\0\indices\QBXMjcCFSWy26Gow1Y9ItQ\4\index ---------- leaf: _0(6.2.0):c1 ---------- <fieldInfo> name: _source, valueType: NONE, indexOptions: NONE <fieldInfo> name: _type, valueType: SORTED_SET, indexOptions: DOCS <fieldInfo> name: _uid, valueType: NONE, indexOptions: DOCS <fieldInfo> name: _version, valueType: NUMERIC, indexOptions: NONE <fieldInfo> name: title, valueType: SORTED_SET, indexOptions: DOCS <fieldInfo> name: num, valueType: SORTED_NUMERIC, indexOptions: NONE <fieldInfo> name: rdate, valueType: SORTED_NUMERIC, indexOptions: NONE <fieldInfo> name: _all, valueType: NONE, indexOptions: DOCS_AND_FREQS_AND_POSITIONS <fieldInfo> name: _field_names, valueType: NONE, indexOptions: DOCS ===== <term> name=_all ===== term=00z, freq=1 term=10, freq=1 term=11, freq=1 term=2016, freq=1 term=20t13, freq=1 term=45, freq=1 term=item1, freq=1 ===== <term> name=_field_names ===== term=_all, freq=1 term=_source, freq=1 term=_type, freq=1 term=_uid, freq=1 term=_version, freq=1 term=num, freq=1 term=rdate, freq=1 term=title, freq=1 ===== <term> name=_type ===== term=data, freq=1 ===== <term> name=_uid ===== term=data#AVfwTjEQFnFWQdd5V9p5, freq=1 ===== <term> name=title ===== term=item1, freq=1
Solr とは、かなり違った結果になっています。