上文找到了 collect(…) 方法,其形参就是匹配的文档 Id,根据代码上下文,其中 doc 是由 iterator.nextDoc() 获得的,那 DefaultBulkScorer.iterator 是何时赋值的?代码如下。

public abstract class Weight implements SegmentCacheable {
protected static class DefaultBulkScorer extends BulkScorer {
// ...
public DefaultBulkScorer(Scorer scorer) {
// ...
this.scorer = scorer;
this.iterator = scorer.iterator();
this.twoPhase = scorer.twoPhaseIterator();
}
// ...
}
}

构造函数中 scorer.iterator() 即为匹配的文档 Id,那么 scorer 又是从何而来呢?回顾 Weight.bulkScorer(…) 方法,代码如下。根据上文可知 scorer(context) 的实现类是 TermWeight。

public abstract class Weight implements SegmentCacheable {
public BulkScorer bulkScorer(LeafReaderContext context) throws IOException {
Scorer scorer = scorer(context);
// ...
return new DefaultBulkScorer(scorer);
}
} public class TermQuery extends Query {
final class TermWeight extends Weight {
@Override
public Scorer scorer(LeafReaderContext context) throws IOException {
final TermsEnum termsEnum = getTermsEnum(context);
if (termsEnum == null) {
return null;
}
PostingsEnum docs = termsEnum.postings(null, needsScores ? PostingsEnum.FREQS : PostingsEnum.NONE);
assert docs != null;
return new TermScorer(this, docs, similarity.simScorer(stats, context));
}
}
} final class TermScorer extends Scorer {
private final PostingsEnum postingsEnum;
TermScorer(Weight weight, PostingsEnum td, Similarity.SimScorer docScorer) {
super(weight);
this.docScorer = docScorer;
this.postingsEnum = td;
}
@Override
public DocIdSetIterator iterator() {
return postingsEnum;
}
}

至此可以确定 scorer.iterator() 来源于 termsEnum.postings(...) 。倒排索引是不是若隐若现了呢。

下面聚焦于 termsEnum 的实际类型和其 postings(...) 方法。

根据上文可知,termsEnum 来源于 TermQuery.getTermsEnum(...),代码如下。

public class TermQuery extends Query {
private TermsEnum getTermsEnum(LeafReaderContext context) throws IOException {
final TermState state = termStates.get(context.ord);
final TermsEnum termsEnum = context.reader().terms(term.field()).iterator();
termsEnum.seekExact(term.bytes(), state);
return termsEnum;
}
} public final class LeafReaderContext extends IndexReaderContext {
private final LeafReader reader;
}

LeafReader 本身是没有 terms(...) 方法的,也就是说 context.reader() 并不是 LeaferReader,而是其子类。根据上文已知 LeafReaderContext 是 IndexSearcher.leafContexts 其中的一个元素,那么找到 IndexSearcher.leafContexts 的赋值代码也就能知道 context.reader() 的实际类型。

public class IndexSearcher {
public IndexSearcher(IndexReader r) {
this(r, null);
} public IndexSearcher(IndexReader r, ExecutorService executor) {
this(r.getContext(), executor);
} public IndexSearcher(IndexReaderContext context, ExecutorService executor) {
// ...
leafContexts = context.leaves();
// ...
}
}

根据这部分代码可知,IndexSearcher.leafContexts 来源于 IndexReader.getContext().leaves()。一般来说,这个 IndexReader 是 DirectoryReader.open(...) 返回的一个 StandardDirectoryReader 类。代码如下。

public abstract class DirectoryReader extends BaseCompositeReader<LeafReader> {
public static DirectoryReader open(final Directory directory) throws IOException {
return StandardDirectoryReader.open(directory, null);
}
}

那么 IndexSearcher.leafContexts 实际来源于 StandardDirectoryReader.getContext().leaves()

public final class StandardDirectoryReader extends DirectoryReader {
// ...
} public abstract class DirectoryReader extends BaseCompositeReader<LeafReader> {
// ...
} public abstract class BaseCompositeReader<R extends IndexReader> extends CompositeReader {
// ...
} public abstract class CompositeReader extends IndexReader {
@Override
public final CompositeReaderContext getContext() {
// ...
readerContext = CompositeReaderContext.create(this);
return readerContext;
} @Override
public List<LeafReaderContext> leaves() throws UnsupportedOperationException {
return leaves;
} private final List<LeafReaderContext> leaves;
}

CompositeReaderContext.create(…) 是怎么创建的呢?

public final class CompositeReaderContext extends IndexReaderContext {
static CompositeReaderContext create(CompositeReader reader) {
return new Builder(reader).build();
} private static final class Builder {
public Builder(CompositeReader reader) {
this.reader = reader;
} public CompositeReaderContext build() {
return (CompositeReaderContext) build(null, reader, 0, 0);
} private IndexReaderContext build(CompositeReaderContext parent, IndexReader reader, int ord, int docBase) {
if (reader instanceof LeafReader) {
final LeafReader ar = (LeafReader) reader;
final LeafReaderContext atomic = new LeafReaderContext(parent, ar, ord, docBase, leaves.size(), leafDocBase);
leaves.add(atomic);
leafDocBase += reader.maxDoc();
return atomic;
} else {
final CompositeReader cr = (CompositeReader) reader;
final List<? extends IndexReader> sequentialSubReaders = cr.getSequentialSubReaders();
final List<IndexReaderContext> children = Arrays.asList(new IndexReaderContext[sequentialSubReaders.size()]);
final CompositeReaderContext newParent;
if (parent == null) {
newParent = new CompositeReaderContext(cr, children, leaves);
} else {
newParent = new CompositeReaderContext(parent, cr, ord, docBase, children);
}
int newDocBase = 0;
for (int i = 0, c = sequentialSubReaders.size(); i < c; i++) {
final IndexReader r = sequentialSubReaders.get(i);
children.set(i, build(newParent, r, i, newDocBase));
newDocBase += r.maxDoc();
}
assert newDocBase == cr.maxDoc();
return newParent;
}
}
} private CompositeReaderContext(CompositeReaderContext parent, CompositeReader reader, int ordInParent, int docbaseInParent, List<IndexReaderContext> children, List<LeafReaderContext> leaves) {
this.leaves = leaves == null ? null : Collections.unmodifiableList(leaves);
// ...
}
}

build(...) 时,传入的 reader 类型是 StandardDirectoryReader,将执行 getSequentialSubReaders() 得到其所有子 reader,并以 reader 作为成员变量创建 LeafReaderContext,然后将 LeafReaderContext 加入到 leaves 中。

所以 IndexSearcher.leafContexts 的每个元素 LeafReaderContext 的 reader 即为 StandardDirectoryReader 的 getSequentialSubReaders()

public final class StandardDirectoryReader extends DirectoryReader {
static DirectoryReader open(final Directory directory, final IndexCommit commit) throws IOException {
return new SegmentInfos.FindSegmentsFile<DirectoryReader>(directory) {
@Override
protected DirectoryReader doBody(String segmentFileName) throws IOException {
SegmentInfos sis = SegmentInfos.readCommit(directory, segmentFileName);
final SegmentReader[] readers = new SegmentReader[sis.size()];
boolean success = false;
try {
for (int i = sis.size()-1; i >= 0; i--) {
readers[i] = new SegmentReader(sis.info(i), sis.getIndexCreatedVersionMajor(), IOContext.READ);
} DirectoryReader reader = new StandardDirectoryReader(directory, readers, null, sis, false, false);
success = true; return reader;
}
// ...
}
}.run(commit);
} StandardDirectoryReader(Directory directory, LeafReader[] readers, IndexWriter writer, SegmentInfos sis, boolean applyAllDeletes, boolean writeAllDeletes) throws IOException {
super(directory, readers);
this.writer = writer;
this.segmentInfos = sis;
this.applyAllDeletes = applyAllDeletes;
this.writeAllDeletes = writeAllDeletes;
}
} public abstract class DirectoryReader extends BaseCompositeReader<LeafReader> {
protected DirectoryReader(Directory directory, LeafReader[] segmentReaders) throws IOException {
super(segmentReaders);
this.directory = directory;
}
} public abstract class BaseCompositeReader<R extends IndexReader> extends CompositeReader {
protected BaseCompositeReader(R[] subReaders) throws IOException {
this.subReaders = subReaders;
// ...
}
}

可以分析出,reader 的类型是 SegmentReader,而该类(其实是其父类)确实是有 terms(…) 方法的。代码如下。

public final class SegmentReader extends CodecReader {
// ...
final SegmentCoreReaders core; @Override
public FieldsProducer getPostingsReader() {
return core.fields;
}
} public abstract class CodecReader extends LeafReader implements Accountable {
@Override
public final Terms terms(String field) throws IOException {
return getPostingsReader().terms(field);
}
} final class SegmentCoreReaders {
final FieldsProducer fields; SegmentCoreReaders(Directory dir, SegmentCommitInfo si, IOContext context) throws IOException {
// ...
final Codec codec = si.info.getCodec();
final PostingsFormat format = codec.postingsFormat();
fields = format.fieldsProducer(segmentReadState);
// ...
}
}

在 lucene-7.3.0 中默认的 codec 是 Lucene70Codec,默认 postingsFomat 是 Lucene50PostingsFormat,这个分析过程请见 Lucene 源码分析之 segment(后续补上)。

所以 SegmentReader.terms(…) 实际调用的是 Lucene50PostingsFormat.fieldsProducer(…).terms(…)。

public final class Lucene50PostingsFormat extends PostingsFormat {
@Override
public FieldsProducer fieldsProducer(SegmentReadState state) throws IOException {
PostingsReaderBase postingsReader = new Lucene50PostingsReader(state);
FieldsProducer ret = new BlockTreeTermsReader(postingsReader, state);
return ret;
}
}

最终 SegmentReader.terms(…) 实际调用的是 BlockTreeTermsReader.terms(…)。

public final class BlockTreeTermsReader extends FieldsProducer {
@Override
public Terms terms(String field) throws IOException {
return fields.get(field);
} private final TreeMap<String,FieldReader> fields = new TreeMap<>(); public BlockTreeTermsReader(PostingsReaderBase postingsReader, SegmentReadState state) throws IOException {
this.postingsReader = postingsReader;
fields.put(fieldInfo.name, new FieldReader(...));
}
}

则 BlockTreeTermsReader.terms(…) 实际返回的是 FieldReader。

再次回顾上文中的核心代码。

public class TermQuery extends Query {
final class TermWeight extends Weight {
@Override
public Scorer scorer(LeafReaderContext context) throws IOException {
final TermsEnum termsEnum = getTermsEnum(context);
if (termsEnum == null) {
return null;
}
PostingsEnum docs = termsEnum.postings(null, needsScores ? PostingsEnum.FREQS : PostingsEnum.NONE);
assert docs != null;
return new TermScorer(this, docs, similarity.simScorer(stats, context));
}
} private TermsEnum getTermsEnum(LeafReaderContext context) throws IOException {
final TermState state = termStates.get(context.ord);
final TermsEnum termsEnum = context.reader().terms(term.field()).iterator();
termsEnum.seekExact(term.bytes(), state);
return termsEnum;
}
}

则 termsEnum 为 FieldReader.iterator(),是一个 SegmentTermsEnum。

public final class FieldReader extends Terms implements Accountable {
@Override
public TermsEnum iterator() throws IOException {
return new SegmentTermsEnum(this);
}
}

则 termsEnum.postings(…) 为 SegmentTermsEnum.postings(…)。

final class SegmentTermsEnum extends TermsEnum {
@Override
public PostingsEnum postings(PostingsEnum reuse, int flags) throws IOException {
currentFrame.decodeMetaData();
return fr.parent.postingsReader.postings(fr.fieldInfo, currentFrame.state, reuse, flags);
} final FieldReader fr;
} public final class FieldReader extends Terms implements Accountable {
final BlockTreeTermsReader parent;
} public final class BlockTreeTermsReader extends FieldsProducer {
final PostingsReaderBase postingsReader;
}

fr 是在 SegmntTermsEnum 的构造函数里出现的。

final class SegmentTermsEnum extends TermsEnum {
public SegmentTermsEnum(FieldReader fr) throws IOException {
this.fr = fr;
}
}

而这个 FieldReader 是在 BlockTreeTermsReader 的构造函数里构造的。

public final class BlockTreeTermsReader extends FieldsProducer {
public BlockTreeTermsReader(PostingsReaderBase postingsReader, SegmentReadState state) throws IOException {
// ...
fields.put(fieldInfo.name, new FieldReader(this,...));
}
} public final class FieldReader extends Terms implements Accountable {
FieldReader(BlockTreeTermsReader parent,...) throws IOException {
this.parent = parent;
}
}

则 fr.parent 是 BlockTreeTermsReader,则 fr.parent.postingsReader 是 Lucene50PostingsReader,这就是倒排索引的核心类。

Lucene 源码分析之倒排索引(三)的更多相关文章

  1. Lucene 源码分析之倒排索引(一)

    倒排索引是 Lucene 的核心数据结构,该系列文章将从源码层面(源码版本:Lucene-7.3.0)分析.该系列文章将以如下的思路展开. 什么是倒排索引? 如何定位 Lucene 中的倒排索引? 倒 ...

  2. Lucene 源码分析之倒排索引(二)

    本文以及后面几篇文章将讲解如何定位 Lucene 中的倒排索引.内容很多,唯有静下心才能跟着思路遨游. 我们可以思考一下,哪个步骤与倒排索引有关,很容易想到检索文档一定是要查询倒排列表的,那么就从此处 ...

  3. 手机自动化测试:appium源码分析之bootstrap三

    手机自动化测试:appium源码分析之bootstrap三   研究bootstrap源码,我们可以通过代码的结构,可以看出来appium的扩展思路和实现方式,从中可以添加我们自己要的功能,针对app ...

  4. 一个lucene源码分析的博客

    ITpub上的一个lucene源码分析的博客,写的比较全面:http://blog.itpub.net/28624388/cid-93356-list-1/

  5. jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——词法解析

    jQuery源码9600多行,而Sizzle引擎就独占近2000行,占了1/5.Sizzle引擎.jQuery事件机制.ajax是整个jQuery的核心,也是jQuery技术精华的体现.里面的有些策略 ...

  6. linux中断源码分析 - 中断发生(三)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 回顾 上篇文章linux中断源码分析 - 初始化(二)已经描述了中断描述符表和中断描述符数组的初始化,由于在初始 ...

  7. lucene源码分析的一些资料

    针对lucene6.1较新的分析:http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/conansonic/article/d ...

  8. Netty源码分析之NioEventLoop(三)—NioEventLoop的执行

    前面两篇文章Netty源码分析之NioEventLoop(一)—NioEventLoop的创建与Netty源码分析之NioEventLoop(二)—NioEventLoop的启动中我们对NioEven ...

  9. InnoDB源码分析--缓冲池(三)

    转载请附原文链接:http://www.cnblogs.com/wingsless/p/5582063.html 昨天写到了InnoDB缓冲池的预读:<InnoDB源码分析--缓冲池(二)> ...

随机推荐

  1. 初识JAVA——流程控制之if语句

    if语句的流程控制主要分为3种:1,单分支结构:if(){……}: 2,双分支结构:if(){……}else{……}; 3,多分枝结构:if(){……}else if(){……}…… 其中作为if语句 ...

  2. 使用LSTM和Softmx来进行意图识别

    前言 在前面我们大致介绍了什么是意图识别,把这个问题抽象出来其实是一个分类问题.在结构上面,我们使用LSTM来提取特征,Softmax来进行最后的多分类.由于语料的限制,我们目前仅考虑电台,音乐,问答 ...

  3. [转]FFMpeg框架代码阅读

    简介 FFmpeg是一个集录制.转换.音/视频编码解码功能为一体的完整的开源解决方案. FFmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用.FFmpeg支持MPEG.Di ...

  4. Failed to complete obtain psql count Master gp_segment_configuration Script Exiti

    问题: 在初始化过程中,如到以下问题: gpadmin-[FATAL]:-Failed to complete obtain psql count Master gp_segment_configur ...

  5. es6(六):module模块(export,import)

    es6之前,社区模块加载方案,主要是CommonJS(用于服务器)和AMD(用于浏览器) 而es6实现的模块解决方案完全可以替代CommonJS和AMD ES6模块设计思想:尽量静态化,在编译时就能确 ...

  6. JS中的top是什么?

    <iframe/>或者<frame>里面用主页面的东西,就是top.xxx如:<script> function func(){ ... };</script ...

  7. 4sumii

    problem description: there is four number list named A,B,C,D; now you should out put the num of  tup ...

  8. 对cordova插件配置文件plugin.xml的理解

    1.配置文件表头包括了插件id,是用于唯一标识插件的.同时插件配置了一个插件名称. 2.这个文件从工作机制,也就是js代码一直到native的java插件代码工作分成两个流程.第一个流程是从代码到插件 ...

  9. js实现地图打印功能

    注意:js对地图的打印功能在arcgis10.1中才有提供,所以如果要使用esri自带的地图打印功能,必须使用arcgis 10.1或更高版本的地图打印模板.(由于官网和arcgis desktop提 ...

  10. Kafka安装之 Zookeeper

    一 . Zookeeper 概述        ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它 ...