使用Java的Lucene搜索工具对检索结果进行分组和分页

 更新时间:2016年03月06日 08:52:56   作者:小檀  
这篇文章主要介绍了使用Java的搜索工具Lucene对检索结果进行分组和分页的方法,Luence是Java环境中的一个全文检索引擎工具包,需要的朋友可以参考下

使用GroupingSearch对搜索结果进行分组
Package org.apache.lucene.search.grouping Description

这个模块可以对Lucene的搜索结果进行分组,指定的单值域被聚集到一起。比如,根据”author“域进行分组,“author”域值相同的的文档分成一个组。

进行分组的时候需要输入一些必要的信息:

1、groupField:根据这个域进行分组。比如,如果你使用“author”域进行分组,那么每一个组里面的书籍都是同一个作者。没有这个域的文档将被分到一个单独的组里面。

2、groupSort:组排序。

3、topNGroups:保留多少组。比如,10表示只保留前10组。

4、groupOffset:对排在前面的哪些分组组进行检索。比如,3表示返回7个组(假设opNGroups等于10)。在分页里面很有用,比如每页只显示5个组。

5、withinGroupSort:组内文档排序。注意:这里和groupSort的区别

6、withingroupOffset:对每一个分组里面的哪些排在前面的文档进行检索。


使用GroupingSearch 对搜索结果分组比较简单

GroupingSearch API文档介绍:

Convenience class to perform grouping in a non distributed environment.

非分布式环境下分组

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) 指定每组返回的文档数,不指定时,默认返回一个文档
  • 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("搜索命中数:" + result.totalHitCount);
    System.out.println("搜索结果分组数:" + result.groups.length);
 
    Document document;
    for (GroupDocs<BytesRef> groupDocs : result.groups) {
      System.out.println("分组:" + groupDocs.groupValue.utf8ToString());
      System.out.println("组内记录:" + 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,"红薯", "开源社区");
    indexHelper.createIndexForGroup(3,"红薯", "代码设计");
    indexHelper.createIndexForGroup(4,"红薯", "设计");
    indexHelper.createIndexForGroup(5,"觉先", "Lucene开发");
    indexHelper.createIndexForGroup(6,"觉先", "Lucene实战");
    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.测试结果:

20163684827254.png (1168×355)

两种分页方式
Lucene有两种分页方式:

1、直接对搜索结果进行分页,数据量比较少的时候可以用这种方式,分页代码核心参照:


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++) {
//对搜索结果数据进行处理的代码
}

2、使用searchAfter(...)

Lucene提供了五个重载方法,可以根据需要使用

20163684904821.png (1012×281)

ScoreDoc after:为上次搜索结果ScoreDoc总量减1;

Query query:查询方式

int n:为每次查询返回的结果数,即每页的结果总量

一个简单的使用示例:

//可以使用Map保存必要的搜索结果
Map<String, Object> resultMap = new HashMap<String, Object>();
ScoreDoc after = null;
Query query = XX
TopDocs td = search.searchAfter(after, query, size);
 
//获取命中数
resultMap.put("num", td.totalHits);
 
ScoreDoc[] sd = td.scoreDocs;
for (ScoreDoc scoreDoc : sd) {
//经典的搜索结果处理
}
//搜索结果ScoreDoc总量减1
after = sd[td.scoreDocs.length - 1]; 
//保存after用于下次搜索,即下一页开始 
resultMap.put("after", after);
 
return resultMap;

相关文章

  • String实例化及static final修饰符实现方法解析

    String实例化及static final修饰符实现方法解析

    这篇文章主要介绍了String实例化及static final修饰符实现方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • Mybatis自定义类型转换器的使用技巧

    Mybatis自定义类型转换器的使用技巧

    这篇文章主要介绍了Mybatis自定义类型转换器的使用技巧,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • win10安装JDK14.0.2的详细安装过程

    win10安装JDK14.0.2的详细安装过程

    这篇文章主要介绍了win10安装JDK14.0.2的详细安装过程的相关资料,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • MyBatis中XML映射器的实现

    MyBatis中XML映射器的实现

    MyBatis的真正强大在于它的语句映射,映射器的XML文件就显得相对简单,本文主要介绍了MyBatis中XML映射器的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • elasticsearch节点的transport请求发送处理分析

    elasticsearch节点的transport请求发送处理分析

    这篇文章主要为大家介绍了elasticsearch节点的transport请求发送处理分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • Java 如何绕过迭代器遍历时的数据修改异常

    Java 如何绕过迭代器遍历时的数据修改异常

    这篇文章主要介绍了Java 绕过迭代器遍历时的数据修改异常的方法,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-02-02
  • SpringCloud整合Consul的实现

    SpringCloud整合Consul的实现

    这篇文章主要介绍了SpringCloud整合Consul的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Java中加锁的方式代码示例

    Java中加锁的方式代码示例

    这篇文章主要给大家介绍了关于Java中加锁方式的相关资料,我们平时开发的过程中难免遇到多线程操作共享资源的时候,这时候一般可以通过加锁的方式保证操作的安全性,需要的朋友可以参考下
    2023-09-09
  • java.io.UnsupportedEncodingException异常的正确解决方法(亲测有效!)

    java.io.UnsupportedEncodingException异常的正确解决方法(亲测有效!)

    这篇文章主要给大家介绍了关于java.io.UnsupportedEncodingException异常的正确解决方法,文中介绍的办法亲测有效,java.io.UnsupportedEncodingException是Java编程语言中的一个异常类,表示指定的字符集不被支持,需要的朋友可以参考下
    2024-02-02
  • Java中&和&&的区别简单介绍

    Java中&和&&的区别简单介绍

    这篇文章主要介绍了Java中&和&&的区别,&&逻辑与||逻辑或  它们都是逻辑运算符,& 按位与|按位或它们都是位运算符,更多详细内容请需要的小伙伴了解下面文章内容
    2022-01-01

最新评论