注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Koala++'s blog

计算广告学 RTB

 
 
 

日志

 
 

Lucene源代码分析[10]  

2009-07-04 08:14:02|  分类: Lucene |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

    忽然想起来有一个重要的内容没有讲,就是.frq.prx。现在把FieldTest的内容换成:

Document doc1 = new Document();

Field name1 = new Field( "TheField", "hello world bonjour la chine bonjour"

       + "koala", Field.Store.YES, Field.Index.TOKENIZED );

doc1.add( name1 );

    现在已经知道都是要合并的,那我们看一个最简单的情况,就是只有一个Field,只有一个Document,我们看一下它的.frq.prx是什么?我把它们列出来:

00 02 01 01 01 01 01

02 03 04 00 06 03 01

    关于.frq已经提过一次,现在再看一次:

if (postingFreq == 1) // optimize freq=1

    freq.writeVInt(1); // set low bit of doc num.

else {

    freq.writeVInt(0); // the document number

    freq.writeVInt(postingFreq); // frequency in doc

}

也就是如果是1,那么就写1,如果不是1,先写0,再写这个term在文档中的频率,我们可以看到上面列的16进制的内容,因为postings是已经排序过的,那么第一个单词就是bonjour,可以看到这出现过两次,所以是00 02,其它的词都只出现过一次,所以都是01

.prx内容中第一个是02,这个没有问题,bonjour是第2term(0),第二个是03,它表示它与上一个bonjour隔了三个单词,看代码:

int lastPosition = 0; // write positions

int[] positions = posting.positions;

for (int j = 0; j < postingFreq; j++) { // use delta-encoding

    int position = positions[j];

    prox.writeVInt(position - lastPosition);

    lastPosition = position;

}

    再列出一部分tis文件:

FF FF FF FE 00 00 00 00 00 00 00 06 00 00 00 80

00 00 00 10 00 07 62 6F 6E 6A 6F 75 72 00 01 00

00 00 05 63 68 69 6E 65 00 01 02 02

    前面就不说了,和tii一样,后面的其实也和它一样,红字标出来的第一个00是表示与上一个term没有相同的串(因为它是第一个)07表字符串的长度,62 6F 6E 6A 6F 75 72表示bonjour,接下来的00表示域的序数,01表示出现Document频率,再写入preqPointerproxPointer的差值。这里也看不出什么,我们看红字的最后一部分,01 02 0201chine出现的次数,02 02也就是我们上面讲的,bonjour它出现了两次.frq中是00 02.prx中是02 03,所以和上一次的差值就是02 02。这个例子举的不太好,那几个单词都没有开头相同的,可以自己试一下。

    我们再接着上次的讲:

private final int appendPostings(SegmentMergeInfo[] smis, int n)

       throws IOException {

    int lastDoc = 0;

    int df = 0; // number of docs w/ term

    resetSkip();

    for (int i = 0; i < n; i++) {

       SegmentMergeInfo smi = smis[i];

       TermPositions postings = smi.getPositions();

       int base = smi.base;

       int[] docMap = smi.getDocMap();

       postings.seek(smi.termEnum);

       while (postings.next()) {

           int doc = postings.doc();

           if (docMap != null)

              doc = docMap[doc]; // map around deletions

           doc += base; // convert to merged space

 

           if (doc < lastDoc)

              throw new IllegalStateException("docs out of order");

 

           df++;

 

           if ((df % skipInterval) == 0) {

              bufferSkip(lastDoc);

           }

 

           int docCode = (doc - lastDoc) << 1; // use low bit to flag freq=1

           lastDoc = doc;

 

           int freq = postings.freq();

           if (freq == 1) {

              freqOutput.writeVInt(docCode | 1); // write doc & freq=1

           } else {

              freqOutput.writeVInt(docCode); // write doc

              freqOutput.writeVInt(freq); // write frequency in doc

           }

 

           int lastPosition = 0; // write position deltas

           for (int j = 0; j < freq; j++) {

              int position = postings.nextPosition();

              proxOutput.writeVInt(position - lastPosition);

              lastPosition = position;

           }

       }

    }

    return df;

}

    其中的一个函数getDocMap不清楚是什么意思,看一下:

int[] getDocMap() {

    if (docMap == null) {

       // build array which maps document numbers around deletions

       if (reader.hasDeletions()) {

           int maxDoc = reader.maxDoc();

           docMap = new int[maxDoc];

           int j = 0;

           for (int i = 0; i < maxDoc; i++) {

              if (reader.isDeleted(i))

                  docMap[i] = -1;

              else

                  docMap[i] = j++;

           }

       }

    }

    return docMap;

}

    这里看到如果有需要删除的Document,它将要删除的DocumentdocMap数组中标记为-1。回到appendPosting函数。另外说一下,最好先一个好一点的测试用例,我这里用的是:

Document doc1 = new Document();

Field name1 = new Field( "TheField", "hello world hello",

    Field.Store.YES, Field.Index.TOKENIZED );

doc1.add( name1 );

 

Document doc2 = new Document();

Field name2 = new Field( "TheField", "hello world",

    Field.Store.YES, Field.Index.TOKENIZED );

doc2.add( name2 );

    在我们这种简单的例子中,postings看起来没有什么意义,它只会有一个文档,df就是document frequency的缩写,当然它也就是记录有多少个document有这个term。如果到有skipIntervaldocument了,就加入一个skip pointer,马上再讲。左移是为了把最后一位当作一个标志位。有点刚才介绍的.frq,如果是1,就把docCode后面加上1,如果不是1,那么就先写入docCode再写入频率。而.prx没有什么变化,还是那样写入。看到这里是不是感觉有点火大,怎么你以前讲的都是错的(以前就一个document所以看不出来还有文档信息)

    现在把.frq.prx列出来:

00 02 03 01 03

00 02 00 01 01

    在这个例子中,第一个termhello,第一个doc0docCode也为0,并且freq2,那么写入的就是00 02,第二个doc1,但是hello只出现了一次,它的docCode2再与1,就是03。后面的world在两个doc中都只出现了一次,所以是01 03。再看.prxhellodoc1中出现是第0term.prx开始是00,第二个hello与第一个hello位置差2,所以第二个是02,在doc2中它也是第0个出现,所以第三个是00world的道理相同。

    我们回头把bufferSkip看一下:

private RAMOutputStream skipBuffer = new RAMOutputStream();

private int lastSkipDoc;

private long lastSkipFreqPointer;

private long lastSkipProxPointer;

 

private void resetSkip() {

    skipBuffer.reset();

    lastSkipDoc = 0;

    lastSkipFreqPointer = freqOutput.getFilePointer();

    lastSkipProxPointer = proxOutput.getFilePointer();

}

 

private void bufferSkip(int doc) throws IOException {

    long freqPointer = freqOutput.getFilePointer();

    long proxPointer = proxOutput.getFilePointer();

 

    skipBuffer.writeVInt(doc - lastSkipDoc);

    skipBuffer.writeVInt((int) (freqPointer - lastSkipFreqPointer));

    skipBuffer.writeVInt((int) (proxPointer - lastSkipProxPointer));

 

    lastSkipDoc = doc;

    lastSkipFreqPointer = freqPointer;

    lastSkipProxPointer = proxPointer;

}

 

private long writeSkip() throws IOException {

    long skipPointer = freqOutput.getFilePointer();

    skipBuffer.writeTo(freqOutput);

    return skipPointer;

}

    看起来也蛮简单的,就是第到skipInterval倍数后都将这skipIntervalfreqprox的长度保存,但是skipBuffer是在内存中,是用writeSkip写入文件的,writeSkip是被mergeTermInfo函数调用的,可以看到它是写最后面的,到底是如何skip的,要等到读的时候才知道了。

if (df > 0) {

    // add an entry to the dictionary with pointers to prox and freq files

    termInfo.set(df, freqPointer, proxPointer,

           (int) (skipPointer - freqPointer));

    termInfosWriter.add(smis[0].term, termInfo);

}

    上面是我们还没有分析完的几句代码,我们看到termInfo是用刚才计算的freqprox进行设置,而add函数,我们已经看过了,其中里面的ti.docFreqti.freqPointerti.proxPointer是合并后的结果。

    还剩SegmentMergermerge函数中的mergeNorms没有看:

private void mergeNorms() throws IOException {

    for (int i = 0; i < fieldInfos.size(); i++) {

       FieldInfo fi = fieldInfos.fieldInfo(i);

       if (fi.isIndexed && !fi.omitNorms) {

           IndexOutput output = directory.createOutput(segment + ".f"

+ i);

           try {

              for (int j = 0; j < readers.size(); j++) {

                  IndexReader reader = (IndexReader)

                     s.elementAt(j);

                  int maxDoc = reader.maxDoc();

                  byte[] input = new byte[maxDoc];

                  reader.norms(fi.name, input, 0);

                  for (int k = 0; k < maxDoc; k++) {

                     if (!reader.isDeleted(k)) {

                         output.writeByte(input[k]);

                     }

                  }

              }

           } finally {

              output.close();

           }

       }

    }

}

    这个函数就比较简单了,只是简单地循环写入,不讲了。

 

 

 

  评论这张
 
阅读(1348)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017