对于一次检索,检索的结果应该和查询相关,同时也应该和索引相关,所以lucene中常用
searcher.search(query)
来完成查询功能,searcher考虑了索引,query则考虑了查询的特点。
lucene提供一个Query的抽象类来定义查询。由于查询是和具体的使用环境相关的,所以具有新特点的Query类型可能会不断的添加到系统中,这部分的设计应该较多的考虑扩展的需要。lucene-2.2支持的Query类型总共有下面10种
- TermQuery
- MultiTermQuery
- BooleanQuery
- WildcardQuery
- PhraseQuery
- PrefixQuery
- MultiPhraseQuery
- FuzzyQuery
- RangeQuery
- org.apache.lucene.search.spans.SpanQuery
前面通过对search部分的源代码分析可以看出,最终searcher.search(Query)委托给Hits进行处理,Hits类中的处理如下
Hits(Searcher s, Query q, Filter f) throws IOException {
weight = q.weight(s);
searcher = s;
filter = f;
getMoreDocs(50); // retrieve 100 initially
}
这里 ,q.weight(s)会返回一个Weight对象。
gerMoreDocs(int)中又调用searcher的抽象方法search(),我们以Searcher的一个子类IndexSearcher为例,看一下search()主要完成的工作
public void search(Weight weight, Filter filter,
final HitCollector results) throws IOException{
HitCollector collector = results;
if (filter != null) {
final BitSet bits = filter.bits(reader);
collector = new HitCollector() {
public final void collect(int doc, float score) {
if (bits.get(doc)) { // skip docs not in bits
results.collect(doc, score);
}
}
};
}
Scorer scorer = weight.scorer(reader);
if (scorer == null)
return;
scorer.score(collector);
}
该方法体内,会使用Weight参数返回一个Scorer(打分器),而Scorer.score()会对结果打分,决定最终的查询结果。
显然即使是Searcher的一个具体的子类IndexSearcher,它的search方法针对Query也是完全面向接口,它内部只定义了程序逻辑,具体的使用什么样的Query,什么样的Weight,什么样的Scorer,完全与Searcher无关,这样保证了Searcher类的独立性。
Query定义了weight()的行为, 这里
public Weight weight(Searcher searcher)
throws IOException {
Query query = searcher.rewrite(this);//对本Query对象进行预处理,转换为容易进行操作的Query
Weight weight = query.createWeight(searcher);//具体的Query对象根据不同情况返回一个具体的Weight对象
float sum = weight.sumOfSquaredWeights();
float norm = getSimilarity(searcher).queryNorm(sum);
weight.normalize(norm);
return weight;
}
下面以BooleanQuery为例,说明search()方法中的
Scorer scorer = weight.scorer(reader);
scorer.score(collector);
的执行原理。
下面的UML图简单的说明了具体的Query,Score和Weight类之间的关系
BooleanQuery.createWeight()返回BooleanWeight对象,BooleanWeight的weights成员包含了BooleanQuery经过rewrite()之后的子Query的Weight值。
BooleanWeight.scorer()方法实际返回的是一个BooleanScore2的对象,该对象将
BooleanWeight.weights中的每一个weight返回的Scorer分为三类,分别添加到三个ArrayList类型对象
prohibitedScorers,requiredScorers和optionalScorers中去。
BooleanScorer2.score()方法实际完成两个工作:
1 使用一个BooleanScorer对象,将前面的prohibitedScorers,requiredScorers和optionalScorers添加到BooleanScorer对象的subScore的成员中,该成员是一个SubScore类型的对象,属于链表结构,提供next()来获得所有的Scorer。
2 调用BooleanScorer.score()。所有SubScorer依次处理和该Scorer相关的文档,完成内容的收集。
SubScorer包括一个Scorer对象,并提供了一些精心设计的字段。
整个的BooleanScorer.score()方法实际是通过一系列的SubScorer.score()完成,比较类似于管道的作用。一个大的执行是通过一系列相关的小管道的完成来实现的。
这部分的设计思想很好的贯彻了抽象的概念,将查询的相关因素划分到两个部分,其中一个部分对另一个部分的使用以面向接口进行程序编写,如果处理的次数比较多的话,引入管道,链表的设计,可以确保任意次数的处理。