lucene 範圍搜尋的六種實現方式

來源:互聯網
上載者:User

 

 

範圍搜尋的六種實現方式

       當你想用一些規則(例如時間範圍)來過濾查詢的時候,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();
}

}

來源於http://hi.baidu.com/got_from_jlu/blog/item/54ccc8c7a0f862c039db49b0.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.