域的元数据信息和数据信息

元数据 .fnm

IndexReader.open() -> DirectoryReader.open() -> FindSegmentsFile.run() FindSegmentsFile.doBody() -> new DirectoryReader() -> SegmentReader.get() -> new CoreReaders() -> CoreReaders.openDocStores()

一个段包含多个域,每个域都有一些元数据信息,保存在 .fnm 文件中。

DirectoryReader

DirectoryReader 的构造函数中,在之前读取段的元数据信息后得到 SegmentInfos 对象,然后对每个段构造一个 SegmentReader 对。

DirectoryReader()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SegmentReader[] readers = new SegmentReader[sis.size()];
for (int i = sis.size() - 1; i >= 0; i--) {
boolean success = false;
try {
readers[i] = SegmentReader.get(readOnly, sis.info(i), termInfosIndexDivisor);
success = true;
} finally {
if (!success) {
// Close all readers we had opened:
for (i++; i < sis.size(); i++) {
try {
readers[i].close();
} catch (Throwable ignore) {
// keep going - we want to clean up as much as possible
}
}
}
}
}
initialize(readers);

SegmentReader

SegmentReader.get()
1
2
3
4
5
6
instance.core = new CoreReaders(dir, si, readBufferSize, termInfosIndexDivisor);
if (doOpenStores) {
instance.core.openDocStores(si);
}
instance.loadDeletedDocs();
instance.openNorms(instance.core.cfsDir, readBufferSize);

CoreReaders构造函数

  1. 确定域信息存在哪个段文件里 cfsDir
    得判断下是否是 compound file (.cfs),否则dir就是索引目录。
  2. 读取 .fnm
    读取 .fnm 元数据文件是通过构造一个 FieldInfos 实例,其构造函数中调用了 read() 进行文件读取。
  3. 读取反向信息 term 相关文件 .tis, .tii
    通过构造一个 TermInfosReader 实例完成。
  4. 读取反向信息 .frq 文件和 .prx 文件
CoreReaders()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
CoreReaders(Directory dir, SegmentInfo si, int readBufferSize, int termsIndexDivisor) throws IOException {
segment = si.name;
this.readBufferSize = readBufferSize;
this.dir = dir;
boolean success = false;
try {
Directory dir0 = dir;
if (si.getUseCompoundFile()) {
cfsReader = new CompoundFileReader(dir, segment + "." + IndexFileNames.COMPOUND_FILE_EXTENSION, readBufferSize);
dir0 = cfsReader;
}
cfsDir = dir0;
fieldInfos = new FieldInfos(cfsDir, segment + "." + IndexFileNames.FIELD_INFOS_EXTENSION);
this.termsIndexDivisor = termsIndexDivisor;
TermInfosReader reader = new TermInfosReader(cfsDir, segment, fieldInfos, readBufferSize, termsIndexDivisor);
if (termsIndexDivisor == -1) {
tisNoIndex = reader;
} else {
tis = reader;
tisNoIndex = null;
}
// make sure that all index files have been read or are kept open
// so that if an index update removes them we'll still have them
freqStream = cfsDir.openInput(segment + "." + IndexFileNames.FREQ_EXTENSION, readBufferSize);
if (fieldInfos.hasProx()) {
proxStream = cfsDir.openInput(segment + "." + IndexFileNames.PROX_EXTENSION, readBufferSize);
} else {
proxStream = null;
}
success = true;
} finally {
if (!success) {
decRef();
}
}
}

FieldInfos

FieldInfos 的构造函数中调用其 read()方法读取 .fnm 文件。
| .fnm属性 | 描述 |
|——–|——–|
|FNMVersion|只有Lucene 2.9及以后有这个版本号|
|size|域的数目|
|name|域名|
|bits|代表一系列标志位,表明对此域的索引方式|

bits标志位含义:

  • 倒数第 1 位 isIndexed
    1 表示被索引,0 则不被索引。
  • 倒数第 2 位 storeTermVector
    1 表示保存词向量,0 则不保存。
  • 倒数第 3 位 storePositionsWithTermVector
    1 表示在词向量中保存位置信息, 0 则不保存。
  • 倒数第 4 位 storeOffsetWithTermVector
    1 表示在词向量中保存偏移量信息,0 则不保存。
  • 倒数第 5 位 omitNorms
    1 表示不保存标准化因子,0 则保存。
  • 倒数第 6 位 storePayloads
    1 表示保存 payload,0 则不保存。
  • 倒数第 7 位 omitTermFreqAndPositions
    1 表示不保存 tf 和 position 信息,0 则保存。

位置是基于 Term 的,偏移量是基于 字母或汉字 的。

payload:

  • 索引是以倒排表形式存储的,对于每一个 Term,都保存了包含这个 Term 的一个链表,为了加快查询速度,此链表多用跳跃表进行存储。
  • payload 信息就是存储在倒排表中的,同文档号一起存放,多用于存储与每篇文档相关的一些信息。当然这部分信息也可以存储域里(stored Field),两者从功能上基本是一样的,然而当要存储的信息很多的时候,存放在倒排表里,利用跳跃表,有利于大大提高搜索速度。
read()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void read(IndexInput input, String fileName) throws IOException {
int firstInt = input.readVInt();
if (firstInt < 0) {
// This is a real format
format = firstInt;
} else {
format = FORMAT_PRE;
}
if (format != FORMAT_PRE & format != FORMAT_START) {
throw new CorruptIndexException("unrecognized format " + format + " in file \"" + fileName + "\"");
}
int size;
if (format == FORMAT_PRE) {
size = firstInt;
} else {
size = input.readVInt(); //read in the size
}
for (int i = 0; i < size; i++) {
String name = StringHelper.intern(input.readString());
byte bits = input.readByte();
boolean isIndexed = (bits & IS_INDEXED) != 0;
boolean storeTermVector = (bits & STORE_TERMVECTOR) != 0;
boolean storePositionsWithTermVector = (bits & STORE_POSITIONS_WITH_TERMVECTOR) != 0;
boolean storeOffsetWithTermVector = (bits & STORE_OFFSET_WITH_TERMVECTOR) != 0;
boolean omitNorms = (bits & OMIT_NORMS) != 0;
boolean storePayloads = (bits & STORE_PAYLOADS) != 0;
boolean omitTermFreqAndPositions = (bits & OMIT_TERM_FREQ_AND_POSITIONS) != 0;
addInternal(name, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, omitTermFreqAndPositions);
}
if (input.getFilePointer() != input.length()) {
throw new CorruptIndexException("did not read all bytes from file \"" + fileName + "\": read " + input.getFilePointer() + " vs size " + input.length());
}
}

CoreReaders.openDocStores()

openDocStores()
1
2
fieldsReaderOrig = new FieldsReader(storeDir, storesSegment, fieldInfos, readBufferSize,
si.getDocStoreOffset(), si.docCount);

构造一个FieldsReader实例,其构造方法中创建了一个cloneableFieldsStream和一个cloneableIndexStream,分别用于读取后面的fdtfdx文件。

openDocStores()
1
termVectorsReaderOrig = new TermVectorsReader(storeDir, storesSegment, fieldInfos, readBufferSize, si.getDocStoreOffset(), si.docCount);

构造一个用于读取 term vector 相关文件(tvc, tvd, tvf)的TermVectorsReader实例,下篇博客会用到。

数据 .fdt .fdx

Searcher.doc() -> IndexSearcher.doc() -> IndexReader.document() -> DirectoryReader.document() -> SegmentReader.document() -> FieldsReader.doc()

SegmentReader.getFieldsReader().doc()中调用文件流indexStream读取fdxfieldsStream读取fdt都是在上面CoreReaders.openDocStores()中构造FieldsReader实例时创建的。

域数据文件 .fdt

* 真正保存 Stored Field 信息的文件。
* 一个段中总共有 segment size 个 doc,fdt 文件中共有 segment size 个项,每一项保存一篇 doc 的域信息。

| 域结构属性 | 描述 |
|--------|--------|
|numFields|域编号|
|bits|代表一系列的标志位|
|toRead|VInt,存储域值的长度|
|bytes|阈值|

bits标志位含义:
* 倒数第 1 位 tokenize
    1 表示分词,0 则不分词。
* 倒数第 2 位 binary
    1 表示存储的是二进制数据,0 则存储的是字符串。
* 倒数第 3 位 compress
    1 表示压缩,0 则不压缩。

域索引文件 .fdx

域索引文件也总共有segment size个项,每篇文档都有一个项,每一项都是一个long,大小固定,每一项都是对应的文档在fdt文件中的起始地址的偏移量,这样如果我们想找到第n篇文档的存储域的信息,只要在fdx中找到第n项,然后按照取出的long作为偏移量,就可以在fdt文件中找到对应的存储域的信息。

感谢:
http://www.cnblogs.com/forfuture1978/archive/2009/12/14/1623599.html