Lucene范围搜索的6种实现方式

转载自:http://blog.csdn.net/bestdowt1314/article/details/5837545

范围搜索的六种实现方式

       当你想用一些规则(例如时间范围)来过滤查询的时候,lucene给我们提供了许多方法实现。选择越多意味着灵活性越大,但同时也意味着做出错误选择的机会 也越大。下列代码包含了六种filter的使用方式和性能表现。并加入了选择建议。


import java.io.IOException;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.ConstantScoreRangeQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryFilter;
import org.apache.lucene.search.RangeFilter;
import org.apache.lucene.search.RangeQuery;
import org.apache.lucene.search.TermQuery;

/**
* filter性能测试。
* 测试表明基于RangeQuery的查询是较慢的实现方式。而基于filter的查询则提供了较大灵活性,并且速度稍快。
*/
public class FilterPerformanceTests
{

private static IndexReader reader;
private static IndexSearcher searcher;

public static void main(String[] args) throws Exception
{
reader=IndexReader.open("/indexes/enron");
searcher=new IndexSearcher(reader);

///The query to run in all our tests
TermQuery tq=new TermQuery(new Term("contents","drink"));

//The filter criteria used by all tests 
String filterField="date";
String lowerRange="20000101";
String upperRange="20001012";
int numQueriesPerTest=100;

timeQuery("Plain Term query", tq, null,numQueriesPerTest);


/*
* Method 1: BooleanQuery with mandatory TermQuery and RangeQuery
*
* 平均用时:                      22.5 ms
* 是否变了搜索结果得分:        是
* 注 解:                                                                                                         不推荐。
*                                                         如果RangeQuery的搜索范围太大会导致抛出"too many clauses"的异常!
*                                                         由于缺少filter term,它的性能表现不能让人满意。
*                                                         但是这种方法却被使用的很多,因为这种方法是唯一能完全用QueryParser
*                                                     的语法来表示的。就像被没有读过lucene api的人使用一样。
*/
RangeQuery rq1=new RangeQuery(new Term(filterField,lowerRange),
new Term(filterField,upperRange),true);
BooleanQuery bq1=new BooleanQuery();
bq1.add(new BooleanClause(tq,BooleanClause.Occur.MUST));
bq1.add(new BooleanClause(rq1,BooleanClause.Occur.MUST));
timeQuery("BooleanQuery with range", bq1, null,numQueriesPerTest);


/*
* Method 2: TermQuery with filter passed to searcher
*
* 平均用时:                      4.53 ms
* 是否变了搜索结果得分:        否
* 注解:                                                推荐。
*/             
RangeFilter rf2=new RangeFilter(filterField,lowerRange,upperRange, true,true);
timeQuery("Query + rangefilter", tq, rf2,numQueriesPerTest);


/*
* Method 3: FilteredQuery using TermQuery and RangeFilter
*
* 平均用时:                      4.38 ms
* 是否变了搜索结果得分:           否
* 注解:                                                推荐。最快的选择,并且可以最灵活的使用,因为可以把很多的filter封装在一个Query中。
*/             
RangeFilter rf3=new RangeFilter(filterField,lowerRange,upperRange, true,true);
FilteredQuery fq3=new FilteredQuery(tq,rf3);
timeQuery("FilteredQuery with rangefilter", fq3, null,numQueriesPerTest);



/*
* Method 4: BooleanQuery with mandatory TermQuery and ConstantScoreQuery(takes a filter)
*
* 平均用时:                      4.85 ms
* 是否变了搜索结果得分:          是
* 注解:                                                一个把filter表达成query的方式。(得分为常量)。
*                                                         这并不是一个真正意义上的filter,因为这会被用作一个查询来运行。
*/             
RangeFilter rf4=new RangeFilter(filterField,lowerRange,upperRange, true,true);
ConstantScoreQuery csq4=new ConstantScoreQuery(rf4);
BooleanQuery bq4=new BooleanQuery();
bq4.add(new BooleanClause(tq,BooleanClause.Occur.MUST));
bq4.add(new BooleanClause(csq4,BooleanClause.Occur.MUST));
timeQuery("ConstantScoreQuery ", bq4, null,numQueriesPerTest);



/*
* Method 5: BooleanQuery with mandatory TermQuery and ConstantScoreRangeQuery
*
* 平均用时:                      4.68 ms
* 是否变了搜索结果得分:              是
* 注解:                                                一个较简单的编码方式。ConstantScoreQuery在内部为我们包装了一个RangeFilter。
*/             
ConstantScoreRangeQuery crq5=new ConstantScoreRangeQuery(filterField,lowerRange,upperRange,true,true);
BooleanQuery bq5=new BooleanQuery();
bq5.add(new BooleanClause(tq,BooleanClause.Occur.MUST));
bq5.add(new BooleanClause(crq5,BooleanClause.Occur.MUST));
timeQuery("ConstantScoreRangeQuery ", bq5, null,numQueriesPerTest);


/*
* Method 6: TermQuery with filter of QueryFilter wrapping a RangeQuery
*
* 平均用时:                      0.94 ms
* 是否变了搜索结果得分:        否      
* 注解:                                                        这 个查询表现得更快,但是它和其他的查询比较是不公平的。因为QueryFilter在内部有一个RangeQuery,
*                                                        它缓存了查询结果,放在一个bitset中。在循环搜索中,重用了这个bitset。
*                                                            在更加仿真的测试或者在实际环境中,应用程序的设置和过滤的需求都是变化的,
*                                                        所以这种缓存是没有意义的,不推荐使用。
*                                                            而且内部RangeQuery的使用也会导致“Too many clauses”的异常。
*
*                                                      */             
RangeQuery rq6=new RangeQuery(new Term(filterField,lowerRange),
new Term(filterField,upperRange),true);
QueryFilter qf6=new QueryFilter(rq6);
timeQuery("Query + QueryFilter wrapping a RangeQuery", tq, qf6,numQueriesPerTest);

searcher.close();
reader.close();
}

private static void timeQuery(String queryType,Query tq, 
Filter filter, int numLoops) throws IOException
{
long start=System.currentTimeMillis();
int numDocs=0;
int top3Docs[]=new int[3];
float top3Scores[]=new float[3];
for(int l=0;l<numLoops;l++)
{
Hits h = searcher.search(tq,filter);
numDocs=h.length();

for(int i=0;i<Math.min(top3Docs.length,numDocs);i++)
{
h.doc(i).get("title");
if(l==0)
{
top3Docs[i]=h.id(i);
top3Scores[i]=h.score(i);
}
}
}               
long end=System.currentTimeMillis();
float ave=((float)(end-start))/(float)numLoops;
System.out.println(queryType+ " took avg "+ave+" millis found. numDocs="+numDocs);
System.out.print("/t top docs[score]=");
for(int i=0;i<top3Docs.length;i++)
{
System.out.print(top3Docs[i]+"["+top3Scores[i]+"]/t");
}
System.out.println();
}

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值