概要

LuceneとかSolrを使っている時に、インデックスファイルを見て状況を把握すると手っ取り早いケースなんかがたまにあります。

そうした時に適切な判断ができるように、小規模なインデックスファイルを目視で確認して、それらがどういったファイルなのか軽く確認してみた。

さらっと見ただけのメモ書きなので、適当なことを書いている可能性があります。

@CretedDate 2012/04/28
@Env Lucene3.6

参考URL

各インデックスファイルの概要はここに載ってる
http://lucene.apache.org/core/old_versioned_docs/versions/3_5_0/fileformats.html

出力コード

例として「content」というフィールドを持つ2つのドキュメントを登録してみます。登録した文字列は以下の2つ。

i have a dream
it is a dream deeply

このマーチン・ルーサー・キングには夢がある。

下記のようなコードでインデックスを生成。

IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_36, 
        new WhitespaceAnalyzer(Version.LUCENE_36));
IndexWriter writer = new IndexWriter(FSDirectory.open(new File("index")), conf);

Document doc1 = new Document();
doc1.add(new Field("content", "i have a dream", Field.Store.YES, Field.Index.ANALYZED));

Document doc2 = new Document();
doc2.add(new Field("content", "it is a dream deeply", Field.Store.YES, Field.Index.ANALYZED));

writer.addDocument(doc1);
writer.addDocument(doc2);

writer.close(true);

できあがったインデックスファイル

上記のコードを実行すると、以下のようなファイルが生成されます。

_0.fdt
_0.fdx
_0.fnm
_0.frq
_0.nrm
_0.prx
_0.tii
_0.tis
segments.gen
segments_1

それぞれのファイルの内容をodコマンドで見ながら、どういった情報が格納されているのか見ていってみる。

実際のファイルは「_0」のところの文字列が生成するたびに変化するけど、拡張子は常に一緒。

_0.fdt (Field Data)

fdtはフィールドデータが入っているファイル。なので中身は非常にわかりやすい。

$ od -c _0.fdt 

 \0  \0  \0 003 001  \0 001 016   i       h   a   v   e       a
      d   r   e   a   m 001  \0 001 024   i   t       i   s    
  a       d   r   e   a   m       d   e   e   p   l   y

見ての通り、登録した文字列がそのまま入っている。016と024は文字列長だと思われる。データをストアしない場合はこれらの文字列は出力されない。

_0.fdx(Field Index)

fdxはfdtと2つ合わせて意味を為すらしい。fdxに各フィールドの場所を記録しておくことで、fdtに対するランダムアクセスが可能になるとか。

$ od -td1 _0.fdx

  0    0    0    3    0    0    0    0    0    0    0    4    0    0    0    0
  0    0    0   22

fdtの位置を表す情報は8bitで指定しているらしい。上記の内容だと、1つ目の要素は4byte目、2つ目の要素は22byte目に登場するという意味だろうか。

_0.fnm(Fields)

フィールド名などのフィールドの情報が入っているらしい。

$ od -c _0.fnm

375 377 377 377 017 001  \a   c   o   n   t   e   n   t 001

バージョン、カウント、名前、インデックスに入れられるか、normの設定はどうか、offsetの設定はどうか、みたいな情報が入っているらしい。目視ではとりあえず名前が入っていることだけ分かった。あとは知らない。

_0.frq(Frequencies)

the lists of documents which contain each term along with the frequency of the term in that document と説明文には書いてある。

$ od -td1 _0.frq

 1    3    3    1    3    1    1    3    3

9byte。

登録した単語は、i have a dream it is a dream deeplyの9つ。これをソートすると、a a dream dream deeply have i is it。ということは、こういう意味だろうか。

a(1) a(3) dream(3) dream(1) deeply(3) have(1) i(1) is(3) it(3)

_0.prx(Positions)

単語のポジションを覚えておくファイルらしい。

$ od -td1 _0.prx

 2    2    4    3    3    1    0    1    0

Frequenciesの情報と合わせて考えると、こういうことになる。

a(1-2) a(3-2) dream(3-4) dream(1-3) deeply(3-3) have(1-1) i(1-0) is(3-1) it(3-0)

これを並び替えると、こうなる。

i(1-0)   have(1-1) a(1-2)  dream(1-3)
it(3-0)  is(3-1)   a(3-2)  deeply(3-3)  dream(3-4)

_0.nrm(Norms)

検索の際のスコアに影響するNromの情報。

$ od -c _0.nrm

N   R   M 377   x   w

最初の3byteはNRM固定、次の1byteはバージョンを表すらしい。残りの2byteがなんらかの意味を持ってるのだろうけど、未調査。

_0.tis(Term Infos)

Termの情報を記述しているらしい。

$ od -c _0.tis

 377 377 377 374  \0  \0  \0  \0  \0  \0  \0  \a  \0  \0  \0 200
  \0  \0  \0 020  \0  \0  \0  \n  \0 001   a  \0 002  \0  \0  \0
 006   d   e   e   p   l   y  \0 001 002 002 001 004   r   e   a
   m  \0 002 001 001  \0 004   h   a   v   e  \0 001 002 002  \0
 001   i  \0 001 001 001 001 001   s  \0 001 001 001 001 001   t
  \0 001 001 001

a, deeply, dreamなどの文字列が入っている。あとはそのTermの長さや数が入っているらしい。このファイルは単独で扱っても便利そう。

例えば下記のようにTermInfosReaderを直接(publicじゃないので同一パッケージから)呼ぶと、さらっと各Termのfrequencyが取れるとか。

// TermInfosReaderの初期化
Directory dir = FSDirectory.open(new File("index"));
TermInfosReader termInfosReader = new TermInfosReader(dir, "_0", 
        new FieldInfos(dir, "_0.fnm"), 2048, 1);

// 中のTermsの情報を出力
SegmentTermEnum terms = termInfosReader.terms();
while (terms.next())
    System.out.println(terms.term() + " - " + terms.docFreq());

以下、実行結果。

content:a - 2
content:deeply - 1
content:dream - 2
content:have - 1
content:i - 1
content:is - 1
content:it - 1

まぁ、こんな使い方はしないか。

_0.tii(Term Info Index)

tis(Term Infos)をランダムアクセスで読むためのファイルらしい。

$ od -td1 _0.tii

  -1   -1   -1   -4    0    0    0    0    0    0    0    1    0    0    0 -128
   0    0    0   16    0    0    0   10    0    0   -1   -1   -1   -1   15    0
   0    0   24

1, 16, 10, 24などの数値が見えます。きっとこの辺の数値とtisの内容が絡んでいるのだと思われる。

segments.gen

セグメントの情報を記載している。

$ od -td1 segments.gen 

 -1   -1   -1   -2    0    0    0    0    0    0    0    1    0    0    0    0
  0    0    0    1

上記はセグメントが1個だった場合。ここでもう1回データを投入してcommitすると、下記のように1だった箇所が2変化する。

$ od -td1 segments.gen 

 -1   -1   -1   -2    0    0    0    0    0    0    0    2    0    0    0    0
  0    0    0    2

segments_1

セグメントの情報を記載している。ファイル名の_1の部分はcommitするたびに変化する。