当用户往Lucene中添加一个文档时,Lucene会基于该文档创建倒排索引,具体是以文档中的字段Field
为单位进行逐个处理的。
大致流程就是对文档中的内容以Field
为单位,进行分词处理,并基于处理后的分词(term
)建立倒排索引。lucene中不管对文档,还是对字段进行处理,实际上都是在DefaultIndexingChain
中处理的。
1 创建DefaultIndexingChain
对象
DefaultIndexingChain
对象在整个索引创建阶段仅存在一个。在创建DefaultIndexingChain
对象的时候,比较重要的就是持有一个TermsHash
对象
public DefaultIndexingChain(DocumentsWriterPerThread docWriter) throws IOException {
...
TermsHash termVectorsWriter = new TermVectorsConsumer(docWriter);
//创建一个TermsHash对象
this.termsHash = new FreqProxTermsWriter(docWriter, termVectorsWriter);
}
TermsHash
对象中包含三大内存缓冲池,分别是:
- intPool缓冲池 : 存储执行bytePool/termBytePool的指针
- bytePool缓冲池 : 和termBytePool指向同一块内存空间
- termBytePool缓冲池:存储的是term的[长度,字节值,所在文档ID,词频,偏移量]等信息
TermsHash(DocumentsWriterPerThread docWriter, boolean trackAllocations, TermsHash nextTermsHash) {
this.intPool = new IntBlockPool(docWriter.intBlockAllocator);
this.bytePool = new ByteBlockPool(docWriter.byteBlockAllocator);
if (nextTermsHash != null) {
this.termBytePool = this.bytePool;
nextTermsHash.termBytePool = this.bytePool;
}
}
2 处理文档processDocument
用户往Lucene中添加一个文档后,Lucene会执行DefaultIndexingChain
中的processDocument
逻辑,具体代码如下:
public void processDocument() throws IOException, AbortingException {
...
while(true) {
//遍历文档中包含的所有字段,以字段为单位,调用processField进行处理
IndexableField field = (IndexableField)i$.next();
fieldCount = this.processField(field, fieldGen, fieldCount);
}
...
}
2 处理文档字段processField
在处理每一个字段Field时,Lucene会首先创建一个PerField对象,这个对象的类型是TermsHashPerField
,可以看processField
方法。
private int processField(IndexableField field, long fieldGen, int fieldCount) throws IOException, AbortingException {
//地段名称
String fieldName = field.name();
//字段类型,eg:Stored
IndexableFieldType fieldType = field.fieldType();
//声明一个PerField对象
DefaultIndexingChain.PerField fp = null;
//这里比较重要,在这个方法中创建一个PerField对象
fp = this.getOrAddField(fieldName, fieldType, true);
//对字段的值进行分词处理,并建立倒排索引
fp.invert(field, first);
}
processField
方法中比较重要的逻辑就是创建PerField
对象和分词建立倒排索引,下面分别来看。
2.1 创建PerField
对象
创建PerField
对象,是在getOrAddField
方法中实现的。getOrAddField
方法代码如下。
private DefaultIndexingChain.PerField getOrAddField(String name, IndexableFieldType fieldType, boolean invert) {
//方法中比较重要的逻辑就是new一个PerField对象。
fp = new DefaultIndexingChain.PerField(fi, invert);
//将PerField对象存放在fieldHash数组中
this.fieldHash[hashPos] = fp;
//返回给上层调用者一个PerField对象
return fp;
}
在new PerField
时,会在PerField的构造方法中执行一些额外的逻辑,其中重要的就是将fieldInfo
对象中的信息添加到termsHash
中,并为每一个Field
创建一个TermsHashPerField
对象,该对象由PerField
持有,并最终存在DefaultIndexingChain
的成员变量fieldHash
数组中。PerField
中的关键代码如下:
public PerField(FieldInfo fieldInfo, boolean invert) {
this.setInvertState();
}
void setInvertSta