Lucene索引文件结构:
Lucene的索引由多个不同后缀格式的文件组成,本文准确来说应该是lucene索引倒排表的文件结构,具体的组织形式如下:
field fieldName --域的名称
term termName --term的名称,也就是分出的词
doc docId --文档id,在指定域下,出现指定term(分词)的文档的id
Freq count --term在文档中出现的次数
Pos --属于第几个分词
Offset --term在文档中的偏移
Payload --payload信息
在不同的codec下,文件的后缀名称和存储格式不一样,其中有个simpletext的codec,特别适合用来学习lucene的“黑盒”索引文件,本文就是讲解在SimpleTextCodec编码下的倒排表文件,具体如何指定codec呢?可以参考下面的代码:
SimpleTextCodec stc = new SimpleTextCodec();
IndexWriterConfig config = new
IndexWriterConfig(Version.LUCENE_40,analyzer);
config.setCodec(stc);//设置索引的编码格式
在SimpleTextCodec编码格式下,lucene索引倒排表文件的后缀是.pst,现在我们来索引两个文档来具体看下倒排表的文件内容。
Document doc0 = new Document();
doc0.add(new Field("title", "title hello title", type));
doc0.add(new Field("content", "我是内容", type));
doc0.add(new Field("keyword", "我是关键字", type));
writer.addDocument(doc0);
Document doc1 = new Document();
doc1.add(new Field("title", "title word title title title", type));
doc1.add(new Field("content", "我是内容", type));
doc1.add(new Field("keyword", "我是关键字", type));
writer.addDocument(doc1);
writer.close();
执行完上面的代码,在索引目录中打开一个.pst后缀的倒排表文件,内容如下:
field content
term 我是内容
doc 0
freq 1
pos 0
doc 1
freq 1
pos 0
field keyword
term 我是关键字
doc 0
freq 1
pos 0
doc 1
freq 1
pos 0
field title
term hello
doc 0
freq 1
pos 1
term title
doc 0
freq 2
pos 0
pos 2
doc 1
freq 4
pos 0
pos 2
pos 3
pos 4
term word
doc 1
freq 1
pos 1
END
相信不用我来解释了吧,lucene倒排表索引文件的内容一目了然.
Lucene索引内存结构:
Lucene索引在内存中的结构主要分三个部分,field、term和docId,具体如下:
Field在内存中的组织结构
Field -> fileposition,域名对应在文件中的偏移量,如果是上面的索引,那么这个结构就是:{content:234,keyword:456,title:767}(具体的偏移值是我自己填写的,只是为了说明问题)
具体源代码在:org.apache.lucene.codecs.simpletext.SimpleTextFieldsReader,由于篇幅的原因,我这里句挑出重点的几个源代码片段来说明:
TreeMap<String,Long> fields=readFields(in.clone());//从倒排表读取数据,Map的key值是filed名称,Map的value是域在倒排表文件中的偏移(位置)
那我们来看下readFields()方法:
private TreeMap<String,Long> readFields(IndexInput in) throws IOException {
BytesRef scratch = new BytesRef(10);
TreeMap<String,Long> fields = new TreeMap<String,Long>();
while (true) {
SimpleTextUtil.readLine(in, scratch);//in代表.pst倒排表文件输入流
if (scratch.equals(END)) {
return fields;
} else if (StringHelper.startsWith(scratch, FIELD)) {//读取field
String fieldName = new String(scratch.bytes, scratch.offset + FIELD.length, scratch.length - FIELD.length, "UTF-8");
fields.put(fieldName, in.getFilePointer());
}
}
}
注意代码标红的部分.
Term在内存中的组织结构
一个叫做FST<input,output>的数据结构,中文名是有穷状态转换器,FST的input即term的值,output有三个内容:A)term在倒排表中的偏移量,即在倒排表文件中的位置
B)docFreq,即这个term在多少个文档中出现
C)totalTermFreq,即这个term在文档中出现的总次数
以上面的索引为例,则这个结构是:
FST<{我是内容,258,2,2},{我是关键字,346,2,2},......>,这里”我是内容”是一个term,然后258是这个term在倒排表文件中的偏移量(位置),再接下的两个2分别是这个term出现的文档数和在这些文档中总共出现的次数。
具体源代码在:org.apache.lucene.codecs.simpletext.SimpleTextFieldsReaderd的loadTerms()方法,读者可以自己去看下这个方法,我这里挑出重点:
private FST<PairOutputs.Pair<Long,PairOutputs.Pair<Long,Long>>> fst;//索引在内存中组织结构
private void loadTerms() throws IOException {
//由于篇幅原因,省略多行代码
IndexInput in = SimpleTextFieldsReader.this.in.clone();//倒排表文件
in.seek(termsStart); //定位文件中的位置,这个值就是上面介绍的field保存的偏移量
while(true) {
SimpleTextUtil.readLine(in, scratch);
if (StringHelper.startsWith(scratch, TERM)) {//读取到term
if (lastDocsStart != -1) {
b.add(Util.toIntsRef(lastTerm, scratchIntsRef), outputs.newPair(lastDocsStart,
outputsInner.newPair((long) docFreq, totalTermFreq))); //往FST数据结构中添加term数据,lastTerm是term的字节数组,lastDocsStart是这个term在倒排表文件中的偏移量,docFreq是出现这个term的文档数,totalTermFreq是这个term在这些文档中总共出现的次数
}
lastDocsStart = in.getFilePointer();
}
}
fst = b.finish();//term加载到FST完成
}
docId在内存中的组织结构
docid -> offset,文档id在索引文件(存储文档数据的文件)中的偏移量.具体的数据结构是:[12,23,37,77......],数组的下标就是文档Id,值就是文档在文件中的偏移量。比如我们要取回文档0的值,那我们根据这个数组找到这个文档在文件中的偏移量是12,然后用随机读定位到文件的这个位置,接着就是read方法取回数据了.源代码在org.apache.lucene.codecs.simpletext.SimpleTextStoredFieldsReader的visitDocument()方法,具体大家就自己去看下吧。
lucene搜索时,索引在文件和内存中的状态
现在用一个搜索过程来看下如何加载和使用这些数据,我们使用的搜索条件是:title=hello
Lucene索引在文件和内存中的数据结构大概就是这样,希望本文对你有所帮助