- 浏览: 888631 次
- 性别:
- 来自: 武汉
文章分类
最新评论
-
小宇宙_WZY:
膜拜一下大神,解决了我一个大问题,非常感谢 orz
【解惑】深入jar包:从jar包中读取资源文件 -
JKL852qaz:
感谢,遇到相同的问题!
【解惑】深入jar包:从jar包中读取资源文件 -
lgh1992314:
为什么java中调用final方法是用invokevirtua ...
【解惑】Java动态绑定机制的内幕 -
鲁曼1991:
说的都有道理,protected只能被同一级包的类所调用
【解惑】真正理解了protected的作用范围 -
鲁曼1991:
...
【总结】String in Java
上接《索引创建(2):DocumentWriter处理流程一 》
1.3.2 第二车间——DocInverterPerField
DocInverterPerField 负责对DocFieldProcessorPerThread对象的Fieldable[]数组的内容建立倒排索引,也就是处理同名字的所有Field。但实际上这个类主要解决的是前期工作,比如分词,统计位置信息等。倒排索引结构的核心的工作由TermsHashPerField和 FreqProxTermsWriterPerField (第三车间 ) 来完成。这两个类将在后面的专题中再提及。
DocInverterPerField 核心方法是processFields(Fieldable[] fields)。它负责这几个方面的工作:
(1)将field的value值切分成一个个term
(2)调用FieldInvertState类来存储和统计当前field的所有term出现的位置position和offset信息,并计算该field的boost分值,为所有相同名字的fields的boost与文档的boost的乘积。
(3) 调用TermsHashPerField和 FreqProxTermsWriterPerField 把 每个term 加入倒排索引结构。
Part I src code:
public void processFields(final Fieldable[] fields, final int count) { //FieldInvertState类的职责就是跟踪将要加入索引(index)结构中的词语的位置(position/offset) //首先初始化FieldInvertState类的数据域。 fieldState.reset(docState.doc.getBoost()); //确定Field允许的最大词语数量10000 final int maxFieldLength = docState.maxFieldLength; //确定fields数组是否需要索引(isIndexed) //如果有一个field需要索引,则doInvert=true final boolean doInvert = consumer.start(fields, count); //取出fields[]中的取出当前field(这些field名字相同) for(int i=0;i<count;i++) { final Fieldable field = fields[i]; //当前field需要索引且整个fields数组都需要检索 if (field.isIndexed() && doInvert) { //如果有多个同名的field,则将后面的field的value接到前面的field之后 //即field[1]的第一个token的词语位置要从field[0]开始算起。 if (fieldState.length > 0) fieldState.position += docState.analyzer.getPositionIncrementGap(fieldInfo.name); //当前field不需要分词 if(!field.isTokenized()) { .... //则直接将整个field的值交给TermsHashPerField建立索引 consumer.start(field); try { consumer.add(); success = true; } finally { if (!success) docState.docWriter.setAborting(); .... }else {//当前field需要分词 final TokenStream stream; //确定field在创建的时候是否已经有了一个内容词语的tokenStream final TokenStream streamValue = field.tokenStreamValue(); //field已经有分好词的tokenStream if (streamValue != null) stream = streamValue; else {//field没有分好词的tokenStream final Reader reader; //确定field的内容是否是Reader类型 final Reader readerValue = field.readerValue(); //field内容是Reader类型 if (readerValue != null) reader = readerValue; else { //filed内容不是Reader类型,则判断是否是String String stringValue = field.stringValue(); if (stringValue == null) throw new IllegalArgumentException("field must have either TokenStream, String or Reader value"); perThread.stringReader.init(stringValue); reader = perThread.stringReader; } //用分析器处理当前field(进行分词和过滤),并加入到postingTable stream = docState.analyzer.reusableTokenStream(fieldInfo.name, reader); } ......第二部分源码..... }//end if(需要索引) consumer.finish(); endConsumer.finish(); }//end for(每一个field) }//end processFields
第一部分源码的主要作用就是根据每一个需要检索的field的不同操作方式进行处理。如果field不需要分词,则直接将filed交给TermsHashPerField建立索引结构(code line: 30, 32)。如果field需要分词,则首先判断field的value是不是Reader类型(分析器Analyzer只接受Reader类型数据),不是则将value字符串值包装成Reader类型(code line:57)。再让Analyzer分词得到TokenStream stream(code line : 61)。然后将stream中的每一个token交给 TermsHashPerField建立索引结构(请看后面的第二部分代码)。
我们用上一节的doc1的例子来查看这个stream的结果,其中doc1通过上一节加工成了DocFieldProcessorPerThread fields[]数组。而fields[0]就是指doc1中名字为cotent的field集合,这个集合有两个content field。
content field 1: The lucene is a good IR. I hope I can lean.
stream 的结果显示(已经去停用词了):
token |
type |
offset |
pos |
lucene | <ALPHANUM> | (4,10) |
2 |
good | <ALPHANUM> | (16,20) |
3 |
ir | <ALPHANUM> | (21,23) | 1 |
i | <ALPHANUM> | (25,26) |
1 |
hope | <ALPHANUM> | (27,31) | 1 |
i | <ALPHANUM> | (32,33) |
1 |
can | <ALPHANUM> | (34,37) |
1 |
lean | <ALPHANUM> | (38,42) | 1 |
content field 2: Lucene 3.0 like a teacher. I love it.
stream 的结果显示(已经去停用词了):
token | type |
offset | pos |
lucene | <ALPHANUM> | (0,7) | 1 |
3.0 | <NUM> | (8,11) | 1 |
like | <ALPHANUM> | (12,16) | 1 |
teacher | <ALPHANUM> | (19,26) | 2 |
i | <ALPHANUM> | (28,29) | 1 |
love | <ALPHANUM> | (30,34) | 1 |
Part II src code:
...... 第一部分..... // 将TokenStream内部指针指向第一个token stream.reset(); final int startLength = fieldState.length; try { //记录当前token首字母在文本中的位置,如果token是TokenStream中的第一个词语,则offsetEnd=-1 int offsetEnd = fieldState.offset-1; //获取分词后tokenStream的每一个token的全部信息 boolean hasMoreTokens = stream.incrementToken(); fieldState.attributeSource = stream; //得到当前token的OffsetAttribute属性信息 OffsetAttribute offsetAttribute =fieldState.attributeSource.addAttribute(OffsetAttribute.class); //得到当前token的PositionIncrementAttribute属性信息 PositionIncrementAttribute posIncrAttribute =fieldState.attributeSource.addAttribute(PositionIncrementAttribute.class); //利用TermsHashPerField将每一个token加入倒排索引结构 consumer.start(field); for(;;) { //tokenStream结束 if (!hasMoreTokens) break; //得到当前token的positionIncreament属性 final int posIncr = posIncrAttribute.getPositionIncrement(); //此时fieldState.position表示当前token所在原文本中的词语位置,即token前面有多少个词语 fieldState.position += posIncr; //positionIncreament属性计算的时候就是相隔的词语数量+1,因此统计当前token前面的词语数量的时候,要减1 if (fieldState.position > 0) { fieldState.position--; } if (posIncr == 0) fieldState.numOverlap++; try { //利用TermsHashPerField将当前token以及fieldState当前所记录的位置信息一并加入进倒排索引结构中 consumer.add(); success = true; } finally { if (!success) docState.docWriter.setAborting(); } //准备记录下一个token,因此将当前token算入进去 fieldState.position++; //记录当前token的尾字母在原文本中所在的位置 offsetEnd = fieldState.offset + offsetAttribute.endOffset(); //fieldState.length记录了当前已经处理了的token数量,如果超过了允许的最大数量,则后面的词语将被丢弃,不再加入到索引中。 if (++fieldState.length >= maxFieldLength) { if (docState.infoStream != null) docState.infoStream.println("maxFieldLength " +maxFieldLength+ " reached for field " + fieldInfo.name + ", ignoring following tokens"); break; } //取下一个token hasMoreTokens = stream.incrementToken(); } stream.end(); } finally { stream.close(); }
第二部分源码的主要作用就是循环得到stream(第一部分代码)中的每一个token(code line: 21),计算token在原始文本中的位置(code line: 28,31),并保存在fieldState.position和fieldState.offset中。同时token和fieldState中的统计信息交给TermsHashPerField建立倒排索引结构(code line: 38)。
总结 ,下图 展示了 DocInverterPerField 的作用。它会把不需要分词的field以红色方框的结构(field value)传给TermsHashPerField和 FreqProxTermsWriterPerField 来建立索引。而把需要分词的content field变成一个个蓝色方框的结构(token && position)来建立索引,接下来就是对token建立倒排索引的过程了。请参见《索引创建(4):DocumentWriter 处理流程三 》。
注意,上图蓝色方框的箭头并不是指DocInverterPerField会把他们建立成链表结构。事实上,这些箭头只是为了表明一个个token依次被 TermsHashPerField加入索引结构的。另外,相同名字的field中的词语会依次处理,就如同上面fields[0]和fields[1]。
发表评论
-
【Lucene3.0 初窥】索引文件格式(2):文件结构总体框架
2010-05-02 16:44 4037Lucene使用文件扩展名标识不同的索引文件。如.fnm文件存 ... -
【Lucene3.0 初窥】索引文件格式(1):预备知识
2010-05-02 16:26 3951注意,本专题内容参见《http://lucene.apache ... -
【Lucene3.0 初窥】索引文件格式(5):posting数据[.frq/.prx]
2010-05-02 12:34 3808★ .frq 词语频 ... -
【Lucene3.0 初窥】索引文件格式(4):dictionary数据[.tii/.tis]
2010-04-30 10:57 3518Terms数据 磁盘文件存储细节 从这篇开始 ... -
【Lucene3.0 初窥】索引文件格式(3):Field数据[.fdx/.fdt/.fnm]
2010-04-23 15:12 5110注意:以下文章是参见h ... -
【Lucene3.0 初窥】索引创建(6):关闭IndexWriter
2010-04-23 15:09 42861.5 IndexWriter的关闭细节 In ... -
【Lucene3.0 初窥】索引创建(4):DocumentWriter 处理流程三
2010-04-15 15:36 3549上接《索引创建(3):DocumentWriter 处理流程二 ... -
【Lucene3.0 初窥】索引创建(5):索引数据池及内存数据细节
2010-04-13 13:50 3721上接《索引创建 (2):DocumentWriter处理流程 ... -
【Lucene3.0 初窥】索引创建(2):DocumentWriter 处理流程一
2010-04-08 21:55 3648上接《索引创建(1): IndexWriter索引器》 ... -
【Lucene3.0 初窥】索引创建(1):IndexWriter索引器
2010-04-07 19:11 4822《Lucene索引创建》系列文章将从源代码出发,详细揭示Luc ... -
【Lucene3.0 初窥】数据源内存组织结构—Document/Field
2010-04-07 16:45 3770在检索数据的时候,我们很希望可以检索出数据源的各种信息。就比如 ... -
【Lucene3.0 初窥】文本分析器Analyzer
2010-04-06 14:58 6406一个优秀的IR system要做好的第一件事就是利用自然语言处 ... -
《Introduce to IR》索引创建
2010-04-03 10:41 3372该系列文章是《An Introduce to Inform ... -
《Introduce to IR》布尔检索模型
2010-03-18 09:33 5486该系列文章是《An Introduce to Informat ... -
【Lucene3.0 初窥】Lucene体系结构概述
2010-03-05 11:37 4001Lucene 的基本原理与《 ... -
【Lucene3.0 初窥】全文检索的基本原理
2010-03-04 16:01 5614全文转载:http://blog.csdn.net/forfu ...
相关推荐
lucene3.0 lucene3.0 lucene3.0 lucene3.0 lucene3.0
Lucene3.0创建索引 读取目录下的所有txt文档格式的文件,然后生成一个索引文件到某目录下!
lucene3.0庖丁+索引搜索程序,里面有代码示例
Lucene3.0 使 用 教 程 Lucene3.0 使 用 教 程 Lucene3.0 使 用 教 程 Lucene3.0 使 用 教 程 Lucene3.0 使 用 教 程 Lucene3.0 使 用 教 程 Lucene3.0 使 用 教 程 Lucene3.0 使 用 教 程 Lucene3.0 使 用 教 程 ...
Lucene3.0之查询处理(1):原理和查询类型 各种Query对象详解
Lucene3.0特性Lucene3.0特性
lucene 3.0 API中文帮助,学习的人懂得的
lucene3.0 中文分词器, 庖丁解牛
Lucene 3.0 原理与代码分析完整版
lucene3.0 实例,在jdk1.5,lucene3.0下调式通过,可以直接运行。先运行生成索引文件的class,在运行搜索的class。
lucene3.0的核心jar包文件,lucene3.0的核心jar包文件,lucene3.0的核心jar包文件,lucene3.0的核心jar包文件。
传智播客Lucene3.0课程,Lucene3.0的入门教程.
在使用lucene3与paoding集成的时候可能会出现以下错误: Exception in thread "main" java.lang.AbstractMethodError: org.apache.lucene.analysis.TokenStream.incrementToken()Z at org.apache.lucene.index....
lucene3.0-highlighter.jar lucene3.0的高亮jar包,从lucene3.0源码中导出来的
Lucene学习总结之三:Lucene的索引文件格式(3) Lucene学习总结之四:Lucene索引过程分析(1) Lucene学习总结之四:Lucene索引过程分析(2) Lucene学习总结之四:Lucene索引过程分析(3) Lucene学习总结之四:...
其中总体架构和索引文件格式是Lucene 2.9的,索引过程分析是Lucene 3.0的。 鉴于索引文件格式没有太大变化,因而原文没有更新,原理和架构的文章中引用了前辈的一些图,可能属于早期的Lucene,但不影响对原理和架构...
Lucene 3.0 原理 Lucene 3.0 原理 Lucene 3.0 原理 Lucene 3.0 原理
全面好用的lucene 2.0 api以及lucene 3.0 api帮助文档
该项目中包括创建索引,增删改查索引,以及关键字高亮显示实例~~对于初学者很有帮助,该项目是基于Lucene3.0
Lucene3.0浅析Lucene3.0浅析Lucene3.0浅析Lucene3.0浅析