搜索过程分析

Lucene的搜索过程:

  1. IndexReader 打开索引文件,读取并打开指向索引文件的流。
  2. 用户输入查询语句。
  3. 将查询语句转换为查询对象Query对象树。
  4. 构造Weight对象树,用于计算词的权重(仅计算搜索语句相关的部分)。
  5. 构造Scorer对象树,用于计算打分。
  6. 在构造Scorer对象树的过程中,其叶子节点的 TermScorer 会将词典和倒排表从索引中读出来。
  7. 构造 SumScorer 对象树,为了方便合并倒排表对Scorer对象树的重新组织,得到结果文档集。
  8. 将收集的结果集合打分返回给用户。

IndexReader

1
IndexReader reader = IndexReader.open(FSDirectory.open(indexDir));
  1. 段元数据信息已经被读入到内存中。
    因而索引文件夹中因为新添加文档而新增加的段对已经打开的reader是不可见的。
  2. .del文件已经读入内存。
    因而其他的reader或者writer删除的文档对打开的reader也是不可见的。
  3. 打开的reader已经有inputstream指向cfs文件。
    一个段文件从生成起就不会改变,新添加的文档都在新的段中,删除的文档都在.del中。段之间的合并是生成新的段,而不会改变旧的段,只不过在段的合并过程中,会将旧的段文件删除。

以上三点保证了 IndexReader 的 snapshot 的性质,也即一个IndexReader打开一个索引,就好像对此索引照了一张像,无论背后索引如何改变,此IndexReader在被重新打开之前,看到的信息总是相同的。

严格的来讲,Lucene的文档号仅仅对打开的某个reader有效,当索引发生了变化,再打开另外一个reader的时候,前面reader的文档0就不一定是后面reader的文档0了,因而我们进行查询的时候,从结果中得到文档号的时候,一定要在reader关闭之前应用,一旦reader关闭了,则文档号已经无意义,如果用其他的reader查询这些文档号,得到的可能是不期望的文档。

IndexSearcher

1
IndexSearcher searcher = new IndexSearcher(reader);

IndexSearcher 提供了两个重要的函数:

  • setSimilarity()
    可以自定义 Similarity 对象,从而影响搜索过程的打分。
  • search()
    负责打分的计算和倒排表的合并。
1
TopDocs docs = searcher.search(query, n);

搜索查询对象包括以下过程:

  • 创建 Weight 树,计算 term weight。
  • 创建 scorer 及 SumScorer 树,为合并到排表做准备。
  • 用 SumScorer 进行倒排表合并。
  • 收集文档结果集和计算打分。

创建 Weight 树,计算 Term Weight

1
searcher.createWeight(query);

包括以下过程:

  • 重写 Query 对象树
  • 创建 Term Weight 对象树
  • 计算 Term Weight 得分

重写 rewrite 主要涉及TermQueryMultiTermQuery,真正需要重写的是MultiTermQuery,也即一个 Query 代表多个 Term 参与查询,例如 PrefixQuery 和 FuzzyQuery。

对于此类Query,Lucene不能直接进行查询,必须进行重写处理,有两种方式:

  • 方式一,将MultiTermQuery对应的所有 Term 看成一个 Term,将包含它们的文档号取出来组成一个 docId Set。
    无论多少 Term,都只有一个倒排表参与合并,不会产生 TooManyClauses异常,性能得到提高。
  • 方式二,将多个 Term 组成一个 BooleanQuery,组成 OR 关系。
    涉及的多个 Term 需要根据索引中的tf、idf参与打分计算。

创建 Scorer 和 SumScorer 树

1
searcher.search(Weight, Filter, int);

除了创建 Scorer 对象树之外,还会创建 SumScorer 树来表示各个语句之间的关系,为合并倒排表做准备。

合并倒排表

  • 交集 ConjunctionScorer,多个must
  • 并集 DisjunctionSumScorer,多个should
  • 差集 ReqExclScorer,must + must_not
  • ReqOptSumScorer,must + should

收集文档结果集和计算打分

文档收集器 TopScoreDocCollector,主要作用是先计算文档的得分,然后将 文档放入优先队列中,最后取出前 n 篇文档(得分相同,文档号小的优先)。

QueryParser

1
2
QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "fieldName", analyzer);
Query query = parser.parse(q);

对查询进行解析成Query的过程,涉及到 JavaCC、QueryParser、分词器、查询语法等,从而生成一个 Query 树。


感谢:
http://www.cnblogs.com/forfuture1978/archive/2010/04/04/1704282.html