第二種方法是使用MultiFieldQueryParser,它是QueryParser的子類,它會在後臺程序中實例化一個QueryParser對象,用來針對每一個域進行查詢表達式的解析,而後使用BooleanQuery將查詢結果合併起來。當程序向BooleanQuery添加查詢子句時,默認操做符OR被用於最簡單的解析方法中。爲了實現更好的控制,布爾操做符可使用BooleanClause的常量指定給每一個域。若是須要指定的話可使用BooleanClause.Occur.MUST,若是禁止指定可使用BooleanClause.Occur.MUST_NOT,或者普通狀況爲BooleanClause.Occur.SHOULD。下面的程序展現的是如何建立MultiFieldQueryParser類的方法:
1.2.方案選擇
以上三種方案中,並非第三種方案最好,也不是第一種方案就最差。哪一種實現方式更適合你的應用程序呢?答案是「看狀況」,由於這裏存在一些取捨。全包含域是一個簡單的解決方案——但這個方案只能對搜索結果進行簡單的排序而且可能浪費磁盤空間(程序可能對一樣的文本索引兩次),但這個方案可能會得到最好的搜索性能。
MultiFieldQueryParser生成的BooleanQuery會計算全部查詢所匹配的文檔評分的總和(DisjunctionMaxQuery則只選取最大評分),而後它可以實現針對每一個域的加權。你必須對以上3中解決方案都進行測試,同時須要一塊兒考慮搜索性能和搜索相關性,而後再找出最佳方案。
2.在結果中查詢
2.1.兩種方案
在檢索結果中再次進行檢索,是一個很常見的需求,通常有兩種方案能夠選擇:
①使用QueryFilter把第一個查詢看成一個過濾器處理;
②用BooleanQuery把先後兩個查詢結合起來,而且使用BooleanClause.Occur.MUST。
針對第一種方法,我須要解釋一下。QueryFilter在Lucene的2.x版本中是存在的,可是在3.x中,lucene的API中這個類已經被廢棄了,沒法再找到。若是你的項目使用的是lucene是3.x,可是你又必定要使用QueryFilter,那麼你必須本身建立一個QueryFilter類,而後將2.x中QueryFilter的源代碼複製過來。你可能會說,直接在工程中同時使用lucene2.x和3.x的核心jar文件不就好了嗎。但遺憾的是,一個工程下,是不能同時使用不一樣版本的lucene的。
2.2.QueryFilter方案
上文已經說了,若是必定要使用QueryFilter,因爲lucene2.x中沒有QueryFilter的API,因此本身要寫一個QueryFilter,QueryFilter的源代碼在lucene2.x中是這樣的:
- import org.apache.lucene.search.CachingWrapperFilter;
- import org.apache.lucene.search.Query;
- import org.apache.lucene.search.QueryWrapperFilter;
-
- public class QueryFilter extends CachingWrapperFilter {
-
-
-
-
-
- public QueryFilter(Query query) {
- super(new QueryWrapperFilter(query));
- }
-
- public boolean equals(Object o) {
- return super.equals((QueryFilter) o);
- }
-
- public int hashCode() {
- return super.hashCode() ^ 0x923F64B9;
- }
- }
第一種方案的例子程序以下:
-
- public static void search(String keyword) throws IOException, ParseException {
- QueryParser queryParser = new QueryParser("content",new SimpleAnalyzer());
- Query query = queryParser.parse(keyword.trim());
- QueryFilter filter = new QueryFilter(query);
-
- search(query, filter);
- }
-
-
- public static void searchInResult(String newKeyword, String oldKeyword) throws ParseException, IOException {
- QueryParser queryParser = new QueryParser("content",new SimpleAnalyzer());
- Query query = queryParser.parse(newKeyword.trim());
- Query oldQuery = queryParser.parse(oldKeyword.trim());
- QueryFilter oldFilter = new QueryFilter(oldQuery);
- CachingWrapperFilter filter = new CachingWrapperFilter(oldFilter);
-
- search(query, filter);
- }
-
- private static void search(Query query, Filter filter) throws IOException, ParseException {
- IndexSearcher ins = new IndexSearcher("d:/tesindex");
- Hits hits = ins.search(query, filter);
- for (int i = 0; i < hits.length(); i++) {
- Document doc = hits.doc(i);
- System.out.println(doc.get("content"));
- }
- }
2.3.BooleanQuery方案
使用BooleanQuery來實如今結果中檢索的過程是這樣的,首先經過關鍵字keyword1正常檢索,當用戶須要在檢索結果中再經過關鍵字keyword2檢索的時候,經過構建BooleanQuery,來實現對在結果中檢索的效果。這裏要注意,這兩個關鍵字都要使用BooleanClause.Occur.MUST。
-
- BooleanQuery booleanQuery = new BooleanQuery();
-
- String[] fields = { "phoneType", "name", "category","free" };
- Query multiFieldQuery = new MultiFieldQueryParser(Version.LUCENE_36, fields, analyzer).parse(keyword);
-
- booleanQuery.add(multiFieldQuery, BooleanClause.Occur.MUST);
-
- if(osKeyword != null && !osKeyword.equals("") && !osKeyword.equals("null")){
- TermQuery osQuery = new TermQuery(new Term("phoneType",osKeyword));
-
- booleanQuery.add(osQuery, BooleanClause.Occur.MUST);
- }
3.檢索結果分頁
3.1.兩種方案
經過關鍵字的檢索,當lucene返回多條記錄的時候,每每一個頁面是沒法容納全部檢索結果的,這天然而然就該分頁了。我這裏給出兩種方案,這兩種方法我都是用過。
第一種方法,就是講檢索結果所有封裝在一個Collection中,例如List中,將這個結果傳到前臺,如jsp頁面。而後在這個list中進行分頁顯示;
第二種方法,是使用lucene自帶的分頁工具public TopDocs topDocs(int start,int howMany)。
我認爲,第一種方法不涉及二次查詢,這樣的話就避免了在查詢上的浪費。可是當檢索的結果數據量很大,這樣一次性傳輸這麼多數據到客戶端,而用戶檢索後獲得的結果每每只會查看第一頁的內容,不多去查看第二頁、第三頁以及後面的內容,因此一次性將所有結果傳到前臺,這樣的浪費是很大的。
第二種方法,雖然每次翻頁都意味着一次查詢,表面上浪費了資源,可是因爲lucene的高效,這樣的浪費對整個系統的影響是微乎其微的,可是這個方法避免了方法一中的缺陷。
3.2.分頁實現
-
-
-
-
-
-
- public void paginationQuery(String keyWord,int pageSize,int currentPage) throws ParseException, CorruptIndexException, IOException {
- String[] fields = {"title","content"};
- QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_36,fields,analyzer);
- Query query = queryParser.parse(keyWord);
-
- IndexReader indexReader = IndexReader.open(directory);
- IndexSearcher indexSearcher = new IndexSearcher(indexReader);
-
-
- TopDocs topDocs = indexSearcher.search(query, 100);
- int totalCount = topDocs.totalHits;
- ScoreDoc[] scoreDocs = topDocs.scoreDocs;
-
-
- int begin = pageSize * (currentPage - 1) ;
-
- int end = Math.min(begin + pageSize, scoreDocs.length);
-
-
- for(int i=begin;i<end;i++) {
- int docID = scoreDocs[i].doc;
- Document doc = indexSearcher.doc(docID);
- int id = NumericUtils.prefixCodedToInt(doc.get("id"));
- String title = doc.get("title");
- System.out.println("id is : "+id);
- System.out.println("title is : "+title);
- }
- }
4.高亮檢索結果
針對檢索結果的高亮實現方法,在lucene中提供了響應的工具,這裏使用lucene-highlighter-3.6.2.jar來實現對檢索結果的高亮顯示。
- public void search(String fieldName, String keyword)throws CorruptIndexException, IOException, ParseException {
- searcher = new IndexSearcher(indexPath);
- QueryParser queryParse = new QueryParser(fieldName, analyzer);
- Query query = queryParse.parse(keyword);
- Hits hits = searcher.search(query);
- for (int i = 0; i < hits.length(); i++) {
- Document doc = hits.doc(i);
- String text = doc.get(fieldName);
- SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<font color='red'>", "</font>");
- Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));
- highlighter.setTextFragmenter(new SimpleFragmenter(text.length()));
- if (text != null) {
- TokenStream tokenStream = analyzer.tokenStream(fieldName,new StringReader(text));
- String highLightText = highlighter.getBestFragment(tokenStream,text);
- System.out.println("高亮顯示第 " + (i + 1) + " 條檢索結果以下所示:");
- System.out.println(highLightText);
- }
- }
- searcher.close();
- }
上文的一行判斷語句很重要:if(text != null),若是text爲空,那麼顯示結果不但沒有被高亮,並且獲得的原始結果也會被過濾。能夠再代碼中加上,若是text==null,則讓將原始檢索結果賦給text,從而將結果顯示出來。
5.檢索結果的評分
lucene的評分是有一套本身的機制的,輸入某一個關鍵字,lucene會對命中的記錄進行評分,默認狀況下,分數越高的結果會排在結果的越前面。若是在建立索引的時候,沒有對某個域進行加權,那麼默認分數的上限是5分,若是有對域作加權,檢索結果的評分可能會出現大於5分的狀況。
咱們可使用explain()來看看lucene對檢索結果的評分狀況:
-
- Explanation explanation = indexSearcher.explain(query, docID);
- System.out.println(explanation.toString());
在後臺打印出來的信息以下:
- 2.4342022 = (MATCH) weight(name:books in 71491), product of:
- 0.2964393 = queryWeight(name:books), product of:
- 8.21147 = idf(docFreq=109, maxDocs=149037)
- 0.036100637 = queryNorm