使用Java的Lucene搜索工具對檢索結果進行分組和分頁
使用GroupingSearch對搜索結果進行分組
Package org.apache.lucene.search.grouping Description
這個模塊可以對Lucene的搜索結果進行分組,指定的單值域被聚集到一起。比如,根據(jù)”author“域進行分組,“author”域值相同的的文檔分成一個組。
進行分組的時候需要輸入一些必要的信息:
1、groupField:根據(jù)這個域進行分組。比如,如果你使用“author”域進行分組,那么每一個組里面的書籍都是同一個作者。沒有這個域的文檔將被分到一個單獨的組里面。
2、groupSort:組排序。
3、topNGroups:保留多少組。比如,10表示只保留前10組。
4、groupOffset:對排在前面的哪些分組組進行檢索。比如,3表示返回7個組(假設opNGroups等于10)。在分頁里面很有用,比如每頁只顯示5個組。
5、withinGroupSort:組內(nèi)文檔排序。注意:這里和groupSort的區(qū)別
6、withingroupOffset:對每一個分組里面的哪些排在前面的文檔進行檢索。
使用GroupingSearch 對搜索結果分組比較簡單
GroupingSearch API文檔介紹:
Convenience class to perform grouping in a non distributed environment.
非分布式環(huán)境下分組
WARNING: This API is experimental and might change in incompatible ways in the next release.
這里使用的是4.3.1版本
一些重要的方法:
- GroupingSearch:setCaching(int maxDocsToCache, boolean cacheScores) 緩存
- GroupingSearch:setCachingInMB(double maxCacheRAMMB, boolean cacheScores) 緩存第一次搜索結果,用于第二次搜索
- GroupingSearch:setGroupDocsLimit(int groupDocsLimit) 指定每組返回的文檔數(shù),不指定時,默認返回一個文檔
- GroupingSearch:setGroupSort(Sort groupSort) 指定分組排序
示例代碼:
1.先看建索引的代碼
public class IndexHelper {
private Document document;
private Directory directory;
private IndexWriter indexWriter;
public Directory getDirectory(){
directory=(directory==null)? new RAMDirectory():directory;
return directory;
}
private IndexWriterConfig getConfig() {
return new IndexWriterConfig(Version.LUCENE_43, new IKAnalyzer(true));
}
private IndexWriter getIndexWriter() {
try {
return new IndexWriter(getDirectory(), getConfig());
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public IndexSearcher getIndexSearcher() throws IOException {
return new IndexSearcher(DirectoryReader.open(getDirectory()));
}
/**
* Create index for group test
* @param author
* @param content
*/
public void createIndexForGroup(int id,String author,String content) {
indexWriter = getIndexWriter();
document = new Document();
document.add(new IntField("id",id, Field.Store.YES));
document.add(new StringField("author", author, Field.Store.YES));
document.add(new TextField("content", content, Field.Store.YES));
try {
indexWriter.addDocument(document);
indexWriter.commit();
indexWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.分組:
public class GroupTest
public void group(IndexSearcher indexSearcher,String groupField,String content) throws IOException, ParseException {
GroupingSearch groupingSearch = new GroupingSearch(groupField);
groupingSearch.setGroupSort(new Sort(SortField.FIELD_SCORE));
groupingSearch.setFillSortFields(true);
groupingSearch.setCachingInMB(4.0, true);
groupingSearch.setAllGroups(true);
//groupingSearch.setAllGroupHeads(true);
groupingSearch.setGroupDocsLimit(10);
QueryParser parser = new QueryParser(Version.LUCENE_43, "content", new IKAnalyzer(true));
Query query = parser.parse(content);
TopGroups<BytesRef> result = groupingSearch.search(indexSearcher, query, 0, 1000);
System.out.println("搜索命中數(shù):" + result.totalHitCount);
System.out.println("搜索結果分組數(shù):" + result.groups.length);
Document document;
for (GroupDocs<BytesRef> groupDocs : result.groups) {
System.out.println("分組:" + groupDocs.groupValue.utf8ToString());
System.out.println("組內(nèi)記錄:" + groupDocs.totalHits);
//System.out.println("groupDocs.scoreDocs.length:" + groupDocs.scoreDocs.length);
for (ScoreDoc scoreDoc : groupDocs.scoreDocs) {
System.out.println(indexSearcher.doc(scoreDoc.doc));
}
}
}
3.簡單的測試:
public static void main(String[] args) throws IOException, ParseException {
IndexHelper indexHelper = new IndexHelper();
indexHelper.createIndexForGroup(1,"紅薯", "開源中國");
indexHelper.createIndexForGroup(2,"紅薯", "開源社區(qū)");
indexHelper.createIndexForGroup(3,"紅薯", "代碼設計");
indexHelper.createIndexForGroup(4,"紅薯", "設計");
indexHelper.createIndexForGroup(5,"覺先", "Lucene開發(fā)");
indexHelper.createIndexForGroup(6,"覺先", "Lucene實戰(zhàn)");
indexHelper.createIndexForGroup(7,"覺先", "開源Lucene");
indexHelper.createIndexForGroup(8,"覺先", "開源solr");
indexHelper.createIndexForGroup(9,"散仙", "散仙開源Lucene");
indexHelper.createIndexForGroup(10,"散仙", "散仙開源solr");
indexHelper.createIndexForGroup(11,"散仙", "開源");
GroupTest groupTest = new GroupTest();
groupTest.group(indexHelper.getIndexSearcher(),"author", "開源");
}
}
4.測試結果:

兩種分頁方式
Lucene有兩種分頁方式:
1、直接對搜索結果進行分頁,數(shù)據(jù)量比較少的時候可以用這種方式,分頁代碼核心參照:
ScoreDoc[] sd = XXX;
// 查詢起始記錄位置
int begin = pageSize * (currentPage - 1);
// 查詢終止記錄位置
int end = Math.min(begin + pageSize, sd.length);
for (int i = begin; i < end && i <totalHits; i++) {
//對搜索結果數(shù)據(jù)進行處理的代碼
}
2、使用searchAfter(...)
Lucene提供了五個重載方法,可以根據(jù)需要使用

ScoreDoc after:為上次搜索結果ScoreDoc總量減1;
Query query:查詢方式
int n:為每次查詢返回的結果數(shù),即每頁的結果總量
一個簡單的使用示例:
//可以使用Map保存必要的搜索結果
Map<String, Object> resultMap = new HashMap<String, Object>();
ScoreDoc after = null;
Query query = XX
TopDocs td = search.searchAfter(after, query, size);
//獲取命中數(shù)
resultMap.put("num", td.totalHits);
ScoreDoc[] sd = td.scoreDocs;
for (ScoreDoc scoreDoc : sd) {
//經(jīng)典的搜索結果處理
}
//搜索結果ScoreDoc總量減1
after = sd[td.scoreDocs.length - 1];
//保存after用于下次搜索,即下一頁開始
resultMap.put("after", after);
return resultMap;
相關文章
SpringBoot讀寫xml上傳到AWS存儲服務S3的示例
這篇文章主要介紹了SpringBoot讀寫xml上傳到S3的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-10-10
詳解Spring Boot工程集成全局唯一ID生成器 UidGenerator的操作步驟
本文就在項目中來集成 UidGenerator這一工程來作為項目的全局唯一 ID生成器。接下來通過實例代碼給大家詳解詳解Spring Boot工程集成全局唯一ID生成器 UidGenerator的操作步驟,感興趣的朋友一起看看吧2018-10-10
springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構建)
這篇文章主要介紹了springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構建),需要的朋友可以參考下2020-08-08
Maven默認中央倉庫(settings.xml 配置詳解)
這篇文章主要介紹了Maven默認中央倉庫(settings.xml 配置詳解),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
ConstraintValidator類如何實現(xiàn)自定義注解校驗前端傳參
這篇文章主要介紹了ConstraintValidator類實現(xiàn)自定義注解校驗前端傳參的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
mybatis?plus框架@TableField注解不生效問題及解決方案
最近遇到一個mybatis plus的問題,@TableField注解不生效,導致查出來的字段反序列化后為空,今天通過本文給大家介紹下mybatis?plus框架的@TableField注解不生效問題總結,需要的朋友可以參考下2022-03-03

