- 浏览: 897472 次
- 性别:
- 来自: 武汉
文章分类
最新评论
-
小宇宙_WZY:
膜拜一下大神,解决了我一个大问题,非常感谢 orz
【解惑】深入jar包:从jar包中读取资源文件 -
JKL852qaz:
感谢,遇到相同的问题!
【解惑】深入jar包:从jar包中读取资源文件 -
lgh1992314:
为什么java中调用final方法是用invokevirtua ...
【解惑】Java动态绑定机制的内幕 -
鲁曼1991:
说的都有道理,protected只能被同一级包的类所调用
【解惑】真正理解了protected的作用范围 -
鲁曼1991:
...
【总结】String in Java
1.3 索引创建过程
DocumentsWriter是由IndexWriter调用来负责对多个document建立索引的核心类,但整个索引过程并不是由一个对象来完成的。而是有一系列的对象组成的处理链(IndexingChain)来完成的(这个过程就像流水线生产汽车)。 下面是DocumentWriter开始建立索引的源代码。
//由IndexWriter调用的方法 boolean addDocument(Document doc, Analyzer analyzer){ return updateDocument(doc, analyzer, null); } boolean updateDocument(Document doc, Analyzer analyzer, Term delTerm){ final DocumentsWriterThreadState state = getThreadState(doc, delTerm); final DocState docState = state.docState; docState.doc = doc; docState.analyzer = analyzer; boolean success = false; try { //调用处理链的源头DocFieldProcessorPerThread开始对Document对象建立索引结构 final DocWriter perDoc = state.consumer.processDocument(); finishDocument(state, perDoc); success = true; } ....... }
1.3.1 第一车间——DocFieldProcessorPerThread
DocFieldProcessorPerThread类是索引创建处理链的第一步。其基本任务:将document对象原料中所有相同名字的field合并成一个 DocFieldProcessorPerThread对象 ,然后更新FieldInfo信息,最后对不同名字的Field构成一个 DocFieldProcessorPerThread[]对象数组。这个数组就是下一个车间DocInverterPerField要加工的原料了。
DocFieldProcessorPerThread类完成第一步处理的核心方法就是processDocument()。在介绍这个方法之前,我们先来看看两个重要的类DocFieldProcessorPerField和FieldInfo
(1) DocFieldProcessorPerField 类是一个合并了相同名字Field的类(可见下图黄色区域)。它是后面DocInverterPerField要处理的单位原料。源码如下:
final class DocFieldProcessorPerField { final DocFieldConsumerPerField consumer; //记录field的名字、是否要检索,是否要存储等信息 final FieldInfo fieldInfo; //指向下一个DocFieldProcessorPerField的指针 DocFieldProcessorPerField next; int lastGen = -1; //包含相同名字的field的数量 int fieldCount; //包含的相同名字的field Fieldable[] fields = new Fieldable[1]; public DocFieldProcessorPerField(final DocFieldProcessorPerThread perThread, final FieldInfo fieldInfo) { this.consumer = perThread.consumer.addField(fieldInfo); this.fieldInfo = fieldInfo; } public void abort() { consumer.abort(); } }
(2) FieldInfo 类并不是指一个Field的全部信息,而是相同名字的Field合并之后的信息。合并过程重要通过update()方法将Field的其他不同属性统一起来(可见下图蓝色区域)。部分源码如下:
final class FieldInfo { //Field的相同名字 String name; //是否要索引 boolean isIndexed; //编号 int number; .... //构造器 FieldInfo(..){ ... } //FieldInfo更新的准则是: //原来的Field和新的Field有一个要索引(isIndexed=true),则更新后的也索引。 //如果新的Field不需要索引,则其他操作指标不变 //如果新的Field需要索引,则只要有一个操作指标为真,就更新后的也为真 void update(...){ ... } }
下面我们重点看看processDocument()方法是如何把Document对象加工成DocFieldProcessorPerThread[]数组的。
final class DocFieldProcessorPerThread extends DocConsumerPerThread { //存储最后处理的结构:DocFieldProcessorPerField[]数组 DocFieldProcessorPerField[] fields = new DocFieldProcessorPerField[1]; int fieldCount; //以Field名字作为关键字的DocFieldProcessorPerField哈希表结构 DocFieldProcessorPerField[] fieldHash = new DocFieldProcessorPerField[2]; /** * 扩大DocFieldProcessorPerField的Hash表容量 * 每一次扩大到原来容量的2倍,并且将原来存储的DocFieldProcessorPerField对象顺序移动到Hash的最大位置处 * 比如:原来的容量为2,扩大之后的容量为4,将fieldHash[1]->fieldHash[3],fieldHash[0]->fieldHash[2] */ private void rehash() { .... } /** *第一加工车间处理核心流程 */ public DocumentsWriter.DocWriter processDocument() { //初始化各项数据 consumer.startDocument(); fieldsWriter.startDocument(); //要处理的document对象 final Document doc = docState.doc; assert docFieldProcessor.docWriter.writer.testPoint("DocumentsWriter.ThreadState.init start"); //记录处理过程中生成的DocFieldProcessorPerField的数量 fieldCount = 0; //当前的DocFieldProcessorPerField final int thisFieldGen = fieldGen++; final List<Fieldable> docFields = doc.getFields(); final int numDocFields = docFields.size(); for(int i=0;i<numDocFields;i++) { //得到doc的每个Field Fieldable field = docFields.get(i); final String fieldName = field.name(); //以Field的名字为key,定位到fieldHash[]的位置号hashPos final int hashPos = fieldName.hashCode() & hashMask; //确定fieldHash[]上指定的hashPos位置是否已经有了数据,也就是是否产生冲突 DocFieldProcessorPerField fp = fieldHash[hashPos]; //如果Field的名字不同,但fieldHash[]的hashPos位置产生了Hash冲突,则采用Hash链表结构加入到冲突位置上的链表末尾。 while(fp != null && !fp.fieldInfo.name.equals(fieldName)) fp = fp.next; //如果fieldHash[]的hashPos位置上没有数据,则将新的Field包装成DocFieldProcessorPerField对象加入到Hash表中 if (fp == null) { FieldInfo fi = fieldInfos.add(fieldName, field.isIndexed(), field.isTermVectorStored(),field.isStorePositionWithTermVector(), field.isStoreOffsetWithTermVector(),field.getOmitNorms(), false, field.getOmitTermFreqAndPositions()); fp = new DocFieldProcessorPerField(this, fi); fp.next = fieldHash[hashPos]; fieldHash[hashPos] = fp; totalFieldCount++; //如果DocFieldProcessorPerField的Hash表存储总数量已经尝过了总容量的1/2,则扩大容量 if (totalFieldCount >= fieldHash.length/2) rehash(); }else{ //如果产生了冲突,并且冲突位置上的Field的名字与要加入的Field名字相同,则更新冲突位置上的FieldInfo fp.fieldInfo.update(field.isIndexed(), field.isTermVectorStored(), field.isStorePositionWithTermVector(), field.isStoreOffsetWithTermVector(), field.getOmitNorms(), false, field.getOmitTermFreqAndPositions()); } //如果具有相同名字的Field,则将同名的Field合并到同一个DocFieldProcessorPerField中的Fieldable[]中 if (thisFieldGen != fp.lastGen) { fp.fieldCount = 0; //如果fields[]已经存满,则扩大2倍的fields[]的容量 if (fieldCount == fields.length) { final int newSize = fields.length*2; DocFieldProcessorPerField newArray[] = new DocFieldProcessorPerField[newSize]; System.arraycopy(fields, 0, newArray, 0, fieldCount); fields = newArray; } fields[fieldCount++] = fp; fp.lastGen = thisFieldGen; } //如果具有相同的Field名字,而DocFieldProcessorPerField中的Fieldable[]已经存满,则扩大2倍的此数组容量用于存放相同名字的Field if (fp.fieldCount == fp.fields.length) { Fieldable[] newArray = new Fieldable[fp.fields.length*2]; System.arraycopy(fp.fields, 0, newArray, 0, fp.fieldCount); fp.fields = newArray; } fp.fields[fp.fieldCount++] = field; if (field.isStored()) { fieldsWriter.addField(field, fp.fieldInfo); } } //将fields数组按field名字排序 quickSort(fields, 0, fieldCount-1); //调用下一加工车间DocInverterPerField对每个DocFieldProcessorPerField对象进行处理 for(int i=0;i<fieldCount;i++) fields[i].consumer.processFields(fields[i].fields, fields[i].fieldCount); ....... }
用个图例来说明一下DocFieldProcessorPerThread类所做的工作。我们拿《索引创建(1):IndexWriter索引器 》1.1节前期工作中的doc1来作为DocFieldProcessorPerThread的原料。
原料:Document doc1 (为了说明相同Field的合并工作,我们加了一个相同名字,值不同的content Field)
Field name | Field value | isIndex | isStore |
name | 1 | false | true |
path | e:\\content\\1.txt | false | true |
content | The lucene is a good IR. I hope I can lean. | true | true |
content | Lucene 3.0 like a teacher. I love it. |
true | true |
半成品:
DocFieldProcessorPerField[] fields
注意,上图中的DocFieldProcessorPerField的next域都指向了null。其实,如果有Field1的名字name1与Field2的名字name2满足 HashCode(name1)=HashCode(name2) && !name1.equals(name2) 的情况下。Field2所构成的DocFieldProcessorPerField对象将加在Field1所构成的DocFieldProcessorPerField对象的next链表后面。这种组织方法便于我们在后面要讲到的建立倒排索引的处理。
总结: DocFieldProcessorPerThread 类的作用就是把Document对象加工成 DocFieldProcessorPerField [] (上图黄色区域) 。然后把每个DocField ProcessorPerThread .Fieldable[] (上图红色区域)
交给第二车间DocInverterPerField 的processFields (《索引创建(3):DocmentWriter 处理流程二》 )方法来完成了。
发表评论
-
【Lucene3.0 初窥】索引文件格式(2):文件结构总体框架
2010-05-02 16:44 4086Lucene使用文件扩展名标识不同的索引文件。如.fnm文件存 ... -
【Lucene3.0 初窥】索引文件格式(1):预备知识
2010-05-02 16:26 4013注意,本专题内容参见《http://lucene.apache ... -
【Lucene3.0 初窥】索引文件格式(5):posting数据[.frq/.prx]
2010-05-02 12:34 3872★ .frq 词语频 ... -
【Lucene3.0 初窥】索引文件格式(4):dictionary数据[.tii/.tis]
2010-04-30 10:57 3593Terms数据 磁盘文件存储细节 从这篇开始 ... -
【Lucene3.0 初窥】索引文件格式(3):Field数据[.fdx/.fdt/.fnm]
2010-04-23 15:12 5183注意:以下文章是参见h ... -
【Lucene3.0 初窥】索引创建(6):关闭IndexWriter
2010-04-23 15:09 43701.5 IndexWriter的关闭细节 In ... -
【Lucene3.0 初窥】索引创建(4):DocumentWriter 处理流程三
2010-04-15 15:36 3601上接《索引创建(3):DocumentWriter 处理流程二 ... -
【Lucene3.0 初窥】索引创建(5):索引数据池及内存数据细节
2010-04-13 13:50 3783上接《索引创建 (2):DocumentWriter处理流程 ... -
【Lucene3.0 初窥】索引创建(3):DocumentWriter 处理流程二
2010-04-10 10:27 4115上接《索引创建(2):DocumentWriter处理流 ... -
【Lucene3.0 初窥】索引创建(1):IndexWriter索引器
2010-04-07 19:11 4875《Lucene索引创建》系列文章将从源代码出发,详细揭示Luc ... -
【Lucene3.0 初窥】数据源内存组织结构—Document/Field
2010-04-07 16:45 3826在检索数据的时候,我们很希望可以检索出数据源的各种信息。就比如 ... -
【Lucene3.0 初窥】文本分析器Analyzer
2010-04-06 14:58 6490一个优秀的IR system要做好的第一件事就是利用自然语言处 ... -
《Introduce to IR》索引创建
2010-04-03 10:41 3421该系列文章是《An Introduce to Inform ... -
《Introduce to IR》布尔检索模型
2010-03-18 09:33 5559该系列文章是《An Introduce to Informat ... -
【Lucene3.0 初窥】Lucene体系结构概述
2010-03-05 11:37 4065Lucene 的基本原理与《 ... -
【Lucene3.0 初窥】全文检索的基本原理
2010-03-04 16:01 5711全文转载:http://blog.csdn.net/forfu ...
相关推荐
lucene3.0 lucene3.0 lucene3.0 lucene3.0 lucene3.0
在Lucene3.0中创建索引是一个关键功能,可以帮助用户快速地检索和管理大量的文本数据。本篇文章将详细介绍如何使用Lucene3.0来创建索引,并通过一个具体的例子来演示整个过程。 #### 一、Lucene3.0简介 Lucene是一...
《深入剖析Lucene3.0:庖丁解牛与索引搜索实践》 在IT行业中,搜索引擎技术扮演着至关重要的角色,而Lucene作为一个开源全文检索库,为开发者提供了强大的文本搜索功能。本文将深入探讨Lucene3.0版本,结合“庖丁解...
在Lucene3.0中,查询处理是一个关键环节,涉及多种查询方式和理论模型。以下是对这些概念的详细解释: 1. **查询方式**: - **顺序查询**:是最简单的查询方式,直接遍历索引,效率较低。 - **索引查询**:基于预...
《Lucene 3.0 原理与代码分析完整版》是一本深入解析Lucene 3.0搜索引擎库的专业书籍。Lucene是Apache软件基金会的开源项目,它为Java开发者提供了一个高性能、全文检索的工具包,广泛应用于各种信息检索系统。这...
lucene 3.0 API中文帮助,学习的人懂得的
在 Lucene 3.0 中,索引过程包括分词、字段处理、文档ID分配等步骤,生成的索引文件包括词典、Posting List、Doc IDs 等组件。 2. **分词器(Analyzer)**:Lucene 提供了多种分词器,如 StandardAnalyzer、...
在这个实例中,我们将探讨如何在 JDK 1.5 和 Lucene 3.0 的环境下构建和运行一个简单的搜索引擎。 首先,Lucene 的核心概念包括文档(Document)、字段(Field)、索引(Index)和搜索(Search)。文档是存储信息的...
2. **多线程支持**:在3.0版本中,Lucene增强了多线程处理能力,允许在并发环境中更有效地创建和更新索引。 3. **内存管理优化**:Lucene 3.0改进了内存使用策略,降低了内存占用,同时提升了索引和搜索的性能。 4...
总结,Lucene3.0是全文检索领域的一个强大工具,其索引构建、分词、查询解析、搜索算法等功能在当时具有很高的技术水平,并且具有高度的灵活性和扩展性。通过深入学习和应用Lucene3.0,开发者可以构建出高效、智能的...
本指南主要关注的是Lucene 3.0版本的API,这是一个强大的工具集,用于在Java环境中构建全文搜索引擎。 一、Lucene核心概念 1. 文档(Document):在Lucene中,文档是信息的基本单位,它由一系列字段(Field)组成...
在这个例子中,我们创建了一个索引,包含一个文档,然后搜索包含"Lucene 3.0"的文档。这个简单的示例展示了Lucene的基本用法,实际应用中可以根据需要扩展,例如添加更多的文档字段、实现更复杂的查询逻辑,或者使用...
lucene3.0 中文分词器, 庖丁解牛
1. **索引构建**: Lucene 2.0 提供了 `IndexWriter` 类,用于创建和更新索引。开发者可以使用 `Document` 类来封装待索引的数据,然后通过 `addDocument()` 方法添加到索引中。 2. **查询构造**: 通过 `QueryParser...
总结来说,这个实例项目是学习Lucene 3.0的一个良好起点,涵盖了从基础的索引操作到更复杂的高亮显示功能。通过实践,你可以深入理解Lucene的内部机制,提升全文检索应用的开发能力。记住,理论与实践相结合是掌握...
通过以上内容的学习,你可以掌握 Lucene 3.0 的基本操作,包括如何创建索引、执行查询、优化搜索性能等。同时,了解 Compass 如何简化 Lucene 的使用,以及如何结合实际业务需求来设计和实现一个搜索引擎。在实践中...
本文将重点介绍如何使用 Lucene 3.0 实现全文检索的基本步骤,以及与前一版本 Lucene 2.0 的主要区别。 **1. 安装与环境配置** 首先,你需要下载 Lucene 3.0 的发行版,并将其添加到你的项目类路径中。确保你的开发...