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

Koala++'s blog

计算广告学 RTB

 
 
 

日志

 
 

Lucene源代码分析[9]  

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

  下载LOFTER 我的照片书  |

其实早应该猜到,我们的Directory都是RAMDirecotry也就是说都在内存里,那什么时候写到硬盘中的呢?

writer.close();

    把代码列出来:

/** Flushes all changes to an index and closes all associated files. */

public synchronized void close() throws IOException {

    flushRamSegments();

    ramDirectory.close();

    if (writeLock != null) {

       writeLock.release(); // release write lock

       writeLock = null;

    }

    if (closeDir)

       directory.close();

}

    注释上写的是把所有的改到flush到一个index中,关闭所有相关文件,函数里也可以看到执行完flush之后,ramDirectoryclose了。

/** Merges all RAM-resident segments. */

private final void flushRamSegments() throws IOException {

    int minSegment = segmentInfos.size() - 1;

    int docCount = 0;

    while (minSegment >= 0

           && (segmentInfos.info(minSegment)).dir == ramDirectory) {

       docCount += segmentInfos.info(minSegment).docCount;

       minSegment--;

    }

    if (minSegment < 0

           || // add one FS segment?

           (docCount + segmentInfos.info(minSegment).docCount) >

                  mergeFactor

           || !(segmentInfos.info(segmentInfos.size() - 1).dir ==

 ramDirectory))

       minSegment++;

    if (minSegment >= segmentInfos.size())

       return; // none to merge

    mergeSegments(minSegment);

}

    得到docCountif第二个和第三个条件不理会,然后看mergeSegments

/** Pops segments off of segmentInfos stack down to minSegment, merges them, and pushes the merged index onto the top of the segmentInfos stack. */

private final void mergeSegments(int minSegment) throws IOException {

    mergeSegments(minSegment, segmentInfos.size());

}

    mergeSegments函数也很长,前面一部分是:

final Vector segmentsToDelete = new Vector();

for (int i = minSegment; i < end; i++) {

    SegmentInfo si = segmentInfos.info(i);

    if (infoStream != null)

       infoStream.print(" " + si.name + " (" + si.docCount + " docs)");

    IndexReader reader = SegmentReader.get(si);

    merger.add(reader);

    if ((reader.directory() == this.directory) ||        (reader.directory() == this.ramDirectory))

       segmentsToDelete.addElement(reader);

}

    这里如果我们有这个目录或是它是ramDirectory我们就保存待删除信息,并且将reader加到merge中。

    核心函数merge()

int mergedDocCount = merger.merge();

    列出来看一下:

final int merge() throws IOException {

    int value;

 

    value = mergeFields();

    mergeTerms();

    mergeNorms();

 

    if (fieldInfos.hasVectors())

       mergeVectors();

 

    return value;

}

    看第一个函数mergeFields()

private final int mergeFields() throws IOException {

    fieldInfos = new FieldInfos(); // merge field names

    int docCount = 0;

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

       ......

    }

    fieldInfos.write(directory, segment + ".fnm");

     

    FieldsWriter fieldsWriter = // merge field values

        new FieldsWriter(directory, segment, fieldInfos);

    try {

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

           IndexReader reader = (IndexReader) readers.elementAt(i);

           int maxDoc = reader.maxDoc();

           for (int j = 0; j < maxDoc; j++)

              if (!reader.isDeleted(j)) { // skip deleted docs

                  fieldsWriter.addDocument(reader.document(j));

                  docCount++;

              }

       }

    } finally {

       fieldsWriter.close();

    }

    return docCount;

}

    我们看到一个我们已经知道的文件.fnm,这个函数就不再重复了。

    下面还是一个我们熟悉的类FieldWriter,它产生.fdx.fdt文件,它们用来保存属性为Index.Store.YesField值,这里的reader前面也看到了,是IndexReader对象,这里就不看它到底是怎么读取的了,只要相信它能运行得到我们想要的就可以了。

    看到这里,我们似乎明白了一些了,它首先把文件都保存到RAMDirectory中,再用IndexReader对象把这些文件读取出来,保存到硬盘上。

    我们看merge函数中的第二个函数mergeTerms

private final void mergeTerms() throws IOException {

    try {

       freqOutput = directory.createOutput(segment + ".frq");

       proxOutput = directory.createOutput(segment + ".prx");

       termInfosWriter = new TermInfosWriter(directory, segment,

              fieldInfos, termIndexInterval);

       skipInterval = termInfosWriter.skipInterval;

       queue = new SegmentMergeQueue(readers.size());

 

       mergeTermInfos();

 

    }

}

    又看到我们已经了解的.frq.prx.tis.tii。我们也看到一下还不了解的SegmentMergeQueue对象,它是一个优先队列,后来我们会看到。

    mergeTermInfos就是将.frq.prx.tis.tii这四个合并后的文件产生的函数,它的前面一部分:

int base = 0;

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

    IndexReader reader = (IndexReader) readers.elementAt(i);

    TermEnum termEnum = reader.terms();

    SegmentMergeInfo smi = new SegmentMergeInfo(base, termEnum, reader);

    base += reader.numDocs();

    if (smi.next())

       queue.put(smi); // initialize queue

    else

       smi.close();

}

    可以看到有一个新的类SegmentMergeInfo,自已点进去看一下,这里就不列出了,另外我们看到这个类对象用来初始化queue,刚才说了它是优先队列,它继承自PriorityQueueput函数并没有重写,列出来看一下:

public final void put(Object element) {

    size++;

    heap[size] = element;

    upHeap();

}

    再看一下upHeap函数:

private final void upHeap() {

    int i = size;

    Object node = heap[i]; // save bottom node

    int j = i >>> 1;

    while (j > 0 && lessThan(node, heap[j])) {

       heap[i] = heap[j]; // shift parents down

       i = j;

       j = j >>> 1;

    }

    heap[i] = node; // install saved node

}

    右移1位就是/2while就是折半查找,再看一下lessThan函数结束:

protected final boolean lessThan(Object a, Object b) {

    SegmentMergeInfo stiA = (SegmentMergeInfo) a;

    SegmentMergeInfo stiB = (SegmentMergeInfo) b;

    int comparison = stiA.term.compareTo(stiB.term);

    if (comparison == 0)

       return stiA.base < stiB.base;

    else

       return comparison < 0;

}

    我们猜也猜到了,当然了按字典序排,我们可能忘记的是base值小的,放到前面去。

    再列出merge的后一半:

SegmentMergeInfo[] match = new SegmentMergeInfo[readers.size()];

 

while (queue.size() > 0) {

    int matchSize = 0; // pop matching terms

    match[matchSize++] = (SegmentMergeInfo) queue.pop();

    Term term = match[0].term;

    SegmentMergeInfo top = (SegmentMergeInfo) queue.top();

 

    while (top != null && term.compareTo(top.term) == 0) {

       match[matchSize++] = (SegmentMergeInfo) queue.pop();

       top = (SegmentMergeInfo) queue.top();

    }

 

    mergeTermInfo(match, matchSize); // add new TermInfo

 

    while (matchSize > 0) {

       SegmentMergeInfo smi = match[--matchSize];

       if (smi.next())

           queue.put(smi); // restore queue

       else

           smi.close(); // done with a segment

    }

}

    中间的那个while循环是找到相同termSegmentMergeInfo,保存到match数组中。

    mergeTermInfo函数比较复杂:

private final void mergeTermInfo(SegmentMergeInfo[] smis, int n)

       throws IOException {

    long freqPointer = freqOutput.getFilePointer();

    long proxPointer = proxOutput.getFilePointer();

 

    int df = appendPostings(smis, n); // append posting data

 

    long skipPointer = writeSkip();

 

    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);

    }

}

    这里看到它得到了.frqprx的文件指针,然后就将刚才相同的termSegmentMergeInfo加入到posting中。

 

 

 

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

历史上的今天

评论

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

页脚

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