lucene--DocInverterPerField/DocInverterPerField

来源:互联网 发布:淘宝网男士休闲布鞋 编辑:程序博客网 时间:2024/06/11 21:23

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:

Java代码  收藏代码
  1.  public void processFields(final Fieldable[] fields, final int count)  {  
  2.   
  3.     //FieldInvertState类的职责就是跟踪将要加入索引(index)结构中的词语的位置(position/offset)  
  4.     //首先初始化FieldInvertState类的数据域。  
  5.     fieldState.reset(docState.doc.getBoost());  
  6.       
  7.     //确定Field允许的最大词语数量10000  
  8.     final int maxFieldLength = docState.maxFieldLength;  
  9.   
  10.     //确定fields数组是否需要索引(isIndexed)  
  11.     //如果有一个field需要索引,则doInvert=true  
  12.     final boolean doInvert = consumer.start(fields, count);  
  13.   
  14.     //取出fields[]中的取出当前field(这些field名字相同)  
  15.     for(int i=0;i<count;i++) {  
  16.         final Fieldable field = fields[i];  
  17.         //当前field需要索引且整个fields数组都需要检索  
  18.         if (field.isIndexed() && doInvert) {  
  19.                
  20.              //如果有多个同名的field,则将后面的field的value接到前面的field之后  
  21.              //即field[1]的第一个token的词语位置要从field[0]开始算起。               
  22.              if (fieldState.length > 0)  
  23.                   fieldState.position += docState.analyzer.getPositionIncrementGap(fieldInfo.name);  
  24.          
  25.   
  26.              //当前field不需要分词  
  27.              if(!field.isTokenized()) {  
  28.                    ....  
  29.                    //则直接将整个field的值交给TermsHashPerField建立索引  
  30.                    consumer.start(field);  
  31.                    try {  
  32.                          consumer.add();  
  33.                          success = true;  
  34.                    } finally {  
  35.                    if (!success)  
  36.                        docState.docWriter.setAborting();  
  37.                    ....  
  38.               }else {//当前field需要分词  
  39.                    final TokenStream stream;  
  40.                    //确定field在创建的时候是否已经有了一个内容词语的tokenStream  
  41.                    final TokenStream streamValue = field.tokenStreamValue();  
  42.                    //field已经有分好词的tokenStream  
  43.                    if (streamValue != null)   
  44.                         stream = streamValue;  
  45.                    else {//field没有分好词的tokenStream  
  46.                        final Reader reader;  
  47.                        //确定field的内容是否是Reader类型  
  48.                        final Reader readerValue = field.readerValue();  
  49.                        //field内容是Reader类型  
  50.                        if (readerValue != null)  
  51.                            reader = readerValue;  
  52.                        else {  
  53.                            //filed内容不是Reader类型,则判断是否是String  
  54.                            String stringValue = field.stringValue();  
  55.                            if (stringValue == null)  
  56.                                throw new IllegalArgumentException("field must have either TokenStream, String or Reader value");  
  57.                            perThread.stringReader.init(stringValue);  
  58.                            reader = perThread.stringReader;  
  59.                        }  
  60.                     //用分析器处理当前field(进行分词和过滤),并加入到postingTable  
  61.                     stream = docState.analyzer.reusableTokenStream(fieldInfo.name, reader);  
  62.                   }  
  63.   
  64.   
  65.                ......第二部分源码.....  
  66.   
  67.   
  68.         }//end if(需要索引)  
  69.           
  70.         consumer.finish();  
  71.         endConsumer.finish();  
  72.   
  73.     }//end for(每一个field)  
  74. }//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 poslucene<ALPHANUM>(4,10) 2good<ALPHANUM>(16,20) 3ir<ALPHANUM>(21,23)1i<ALPHANUM>(25,26) 1hope<ALPHANUM>(27,31)1i<ALPHANUM>(32,33) 1can<ALPHANUM>(34,37) 1lean<ALPHANUM>(38,42)1

 

content field 2: Lucene 3.0 like a teacher. I love it.

stream 的结果显示(已经去停用词了):

token type offsetposlucene<ALPHANUM>(0,7)13.0<NUM>(8,11)1like<ALPHANUM>(12,16)1teacher<ALPHANUM>(19,26)2i<ALPHANUM>(28,29)1love<ALPHANUM>(30,34)1

 


Part II src code:

Java代码  收藏代码
  1. ...... 第一部分.....  
  2.   
  3. // 将TokenStream内部指针指向第一个token  
  4. stream.reset();  
  5.   
  6. final int startLength = fieldState.length;  
  7.   
  8. try {  
  9.    //记录当前token首字母在文本中的位置,如果token是TokenStream中的第一个词语,则offsetEnd=-1  
  10.    int offsetEnd = fieldState.offset-1;  
  11.    //获取分词后tokenStream的每一个token的全部信息  
  12.    boolean hasMoreTokens = stream.incrementToken();  
  13.   
  14.     fieldState.attributeSource = stream;  
  15.    //得到当前token的OffsetAttribute属性信息  
  16.    OffsetAttribute offsetAttribute =fieldState.attributeSource.addAttribute(OffsetAttribute.class);  
  17.    //得到当前token的PositionIncrementAttribute属性信息  
  18.    PositionIncrementAttribute posIncrAttribute =fieldState.attributeSource.addAttribute(PositionIncrementAttribute.class);  
  19.    //利用TermsHashPerField将每一个token加入倒排索引结构  
  20.    consumer.start(field);        
  21.    for(;;) {  
  22.       //tokenStream结束  
  23.       if (!hasMoreTokens) break;  
  24.         
  25.       //得到当前token的positionIncreament属性  
  26.       final int posIncr = posIncrAttribute.getPositionIncrement();       
  27.       //此时fieldState.position表示当前token所在原文本中的词语位置,即token前面有多少个词语  
  28.       fieldState.position += posIncr;  
  29.       //positionIncreament属性计算的时候就是相隔的词语数量+1,因此统计当前token前面的词语数量的时候,要减1  
  30.       if (fieldState.position > 0) {  
  31.                 fieldState.position--;  
  32.       }  
  33.         
  34.       if (posIncr == 0)  
  35.                 fieldState.numOverlap++;  
  36.       try {  
  37.           //利用TermsHashPerField将当前token以及fieldState当前所记录的位置信息一并加入进倒排索引结构中             
  38.           consumer.add();  
  39.            success = true;  
  40.       } finally {  
  41.            if (!success)  
  42.                docState.docWriter.setAborting();  
  43.       }  
  44.       //准备记录下一个token,因此将当前token算入进去  
  45.       fieldState.position++;  
  46.       //记录当前token的尾字母在原文本中所在的位置  
  47.       offsetEnd = fieldState.offset + offsetAttribute.endOffset();         
  48.       //fieldState.length记录了当前已经处理了的token数量,如果超过了允许的最大数量,则后面的词语将被丢弃,不再加入到索引中。  
  49.       if (++fieldState.length >= maxFieldLength) {  
  50.                 if (docState.infoStream != null)  
  51.                   docState.infoStream.println("maxFieldLength " +maxFieldLength+ " reached for field " + fieldInfo.name + ", ignoring following tokens");  
  52.                 break;  
  53.        }  
  54.   
  55.       //取下一个token  
  56.       hasMoreTokens = stream.incrementToken();  
  57.     }  
  58.     stream.end();  
  59.               
  60.  } finally {  
  61.      stream.close();  
  62.  }  
 

第二部分源码的主要作用就是循环得到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]。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 发型睡觉压乱了怎么办 通宵一夜第二天怎么办 夏天了腿脚还凉怎么办 咖啡色三天了月经还是下不来怎么办 睡觉姿势不对腰疼怎么办 来月经吃了香瓜怎么办 减肥期间晚上有饭局怎么办 减肥期间遇到晚上聚餐怎么办 婴儿脸不向上睡怎么办 睡觉压奶了疼怎么办 堵奶了挤不下来怎么办 孕36周胎儿腿短怎么办 孕晚期胎儿腿短怎么办 孕晚期宝宝腿短怎么办 手劳累过度麻痛怎么办 大人发烧40不退怎么办? 颈椎扯的脑袋疼怎么办 孕妇颈椎痛导致失眠怎么办 做完运动脊椎中间痛怎么办? 阴虛阴虚火旺怎么办 脸一躺下就丑怎么办 身体淤堵的厉害怎么办 怀孕晚期脚肿了怎么办 宝宝感冒咳嗽流鼻涕流泪怎么办 孕晚期睡觉习惯面朝右怎么办 鼻涕一直不停的流怎么办 宝宝一直流清鼻涕怎么办 感冒了眼睛酸胀流泪怎么办 五个月宝宝感冒流鼻涕怎么办 六个月宝宝感冒流鼻涕怎么办 一岁宝宝感冒咳嗽流鼻涕怎么办 七个月宝宝感冒流鼻涕咳嗽怎么办 孩子握笔姿势不正确怎么办 走久了脚底板痛怎么办 不会给宝宝拍嗝怎么办 打了肉毒素淤青怎么办 新生儿一吃母乳就睡觉怎么办 10岁半宝宝缺钾怎么办 宝宝吃不到乳晕怎么办 喂母乳乳头破了怎么办 宝宝吸了乳头痛怎么办