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

使用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.测试结果:

两种分页方式
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提供了五个重载方法,可以根据需要使用

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;
(0)

相关推荐

  • 使用Lucene.NET实现站内搜索

    导入Lucene.NET 开发包 Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎.Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎.Lucene.Net 是 .NET 版的Lucene. 你可以在这里下载到最新的Lucene.NET 创建索引.更新索引.删除索引 搜索,根据索引查找 IndexHelper

  • Lucene.Net实现搜索结果分类统计功能(中小型网站)

    最近我们搜易站内搜索系统的一个客户需要一个无限级分类和分类统计功能,要实现的效果如下: 但由于搜易站内搜索系统是基于Lucene.net 2.0开发的,并没有内置的分类统计搜索功能,于是乎只能自己实现了,考虑到客户的总数据量和搜索量不是特别大,于是用了简单有效的方式来实现: 因为涉及到分类的操作,但是每个站点的分类体系还是有些不一样的,本文主要提供思路和部分演示代码,给有需要的童鞋参考: 思路: 首先想到Lucene搜索出来的结果是一个Hits对象,Hits其实就是一个搜索结果文档的集合对象,那

  • 使用Lucene实现一个简单的布尔搜索功能

    什么是lucene Lucene是apache软件基金会jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言). Lucene是一个全文搜索框架,而不是应用产品.因此它并不像www.baidu.com 或者google Desktop那么拿来就能用,它只是提供了一种工具让你能实现这些产品. 在布尔查询的对象中,包含一个子句的集合,各个子句间都是如

  • 基于Lucene的Java搜索服务器Elasticsearch安装使用教程

    一.安装Elasticsearch Elasticsearch下载地址:http://www.elasticsearch.org/download/ ·下载后直接解压,进入目录下的bin,在cmd下运行elasticsearch.bat 即可启动Elasticsearch ·用浏览器访问: http://localhost:9200/   ,如果出现类似如下结果则说明安装成功: { "name" : "Benedict Kine", "cluster_na

  • Java实现lucene搜索功能的方法(推荐)

    直接上代码: package com.sand.mpa.sousuo; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverMa

  • 基于ASP.NET的lucene.net全文搜索实现步骤

    在做项目的时候,需求添加全文搜索,选择了lucene.net方向,调研了一下,基本实现了需求,现在将它分享给大家.理解不深请多多包涵. 在完成需求的时候,查看的大量的资料,本文不介绍详细的lucene.net工程建立,只介绍如何对文档进行全文搜索.对于如何建立lucene.net的工程请大家访问 使用lucene.net搜索分为两个部分,首先是创建索引,创建文本内容的索引,其次是根据创建的索引进行搜索.那么如何对文档进行索引呢,主要是对文档的内容进行索引,关键是提取出文档的内容,按照常规实现,由

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

    使用GroupingSearch对搜索结果进行分组 Package org.apache.lucene.search.grouping Description 这个模块可以对Lucene的搜索结果进行分组,指定的单值域被聚集到一起.比如,根据"author"域进行分组,"author"域值相同的的文档分成一个组. 进行分组的时候需要输入一些必要的信息: 1.groupField:根据这个域进行分组.比如,如果你使用"author"域进行分组,那么

  • Python通过90行代码搭建一个音乐搜索工具

    下面小编把具体实现代码给大家分享如下: 之前一段时间读到了这篇博客,其中描述了作者如何用java实现国外著名音乐搜索工具shazam的基本功能.其中所提到的文章又将我引向了关于shazam的一篇论文及另外一篇博客.读完之后发现其中的原理并不十分复杂,但是方法对噪音的健壮性却非常好,出于好奇决定自己用python自己实现了一个简单的音乐搜索工具-- Song Finder, 它的核心功能被封装在SFEngine 中,第三方依赖方面只使用到了 scipy. 工具demo 这个demo在ipython

  • Java 中DateUtils日期工具类的实例详解

    Java 中DateUtils日期工具类的实例详解 介绍 在java中队日期类型的处理并不方便,通常都需要借助java.text.SimpleDateFormat类来实现日期类型 和字符串类型之间的转换,但是在jdk1.8之后有所改善,jdk1.7以及之前的版本处理日期类型并不方便, 可以借助Joda Time组件来处理,尤其是日期类型的一些数学操作就更是不方便. java代码 /** * * 日期工具类 java对日期的操作一直都很不理想,直到jdk1.8之后才有了本质的改变. * 如果使用的

  • java开发时各类工具的使用规范

    工具类规范 一个项目不可能没有工具类,工具类的初衷是良好的,代码重用,但到了后面工具类越来越乱,有些项目工具类有几十个,看的眼花缭乱,还有不少重复.如何编写出好的工具类,我有几点建议: 隐藏实现 就是要定义自己的工具类,尽量不要在业务代码里面直接调用第三方的工具类.这也是解耦的一种体现.如果我们不定义自己的工具类而是直接使用第三方的工具类有2个不好的地方: 不同的人会使用不同的第三方工具库,会比较乱. 将来万一要修改工具类的实现逻辑会很痛苦. 以最简单的字符串判空为例,很多工具库都有 Strin

  • java身份证合法性校验工具类实例代码

    1.身份证规则 计算方法(来源百度) 将前面的身份证号码17位数分别乘以不同的系数.从第一位到第十七位的系数分别为:7-9-10-5-8-4-2-1-6-3-7-9-10-5-8-4-2. 将这17位数字和系数相乘的结果相加. 用加出来和除以11,看余数是多少? 余数只可能有0-1-2-3-4-5-6-7-8-9-10这11个数字.其分别对应的最后一位身份证的号码为1-0-X -9-8-7-6-5-4-3-2.(即余数0对应1,余数1对应0,余数2对应X-) 通过上面得知如果余数是3,就会在身份

  • JetBrains发布java代码质量检测工具Qodana早期预览版

    JetBrains正在开发一种被称为Qodana的代码质量检测工具.它将JetBrains IDE具有的智能代码检查带入了项目CI/CD管道中.它可以识别代码中的错误,安全漏洞,重复项和缺陷并提出修复建议.使开发人员轻松地改善代码结构,使代码符合众多准则和标准,解决潜在的性能问题. Qodana Qodana包括两个主要部分:一个精良的.无GUI的IntelliJ IDEA引擎,该引擎经过专门设计用于CI管道中,作为一种典型的"流水线"工具:另一个基于Web的交互式报表UI. 目前Qo

  • python 制作本地应用搜索工具

    一.准备工作 请确保已经安装tkinter.pyperclip.threading 二.预览 1.启动 这是程序启动的主界面. 2.运行 搜索之后的界面. 3.结果 选择应用,右击鼠标复制它的下载链接. 三.设计思路 四.源代码 本次还是将GUI和搜索引擎分离开来,只要下面两个py文件在一个文件夹,结合已有的数据库就能实现上述功能. 4.1 GUI.py from tkinter import * from tkinter import ttk from tkinter import messa

  • Java JDBC自定义封装工具类的步骤和完整代码

    封装JDBC工具类的作用可以优化代码,提高开发效率 步骤 ① 创建配置文件(config.properties),用于存放注册驱动和连接数据库时所需要的参数值 ② 自定义一个类(JDBCUtils.java) ③ 实现构造私有方法 private JDBCUtils(){} ④ 声明所需要的配置变量 private static String driverClass; private static String url; private static String username; priva

  • java中封装JDBC工具类的实例分析

    对于能够重复使用的代码,我们最好的方法是对它们进行封装,然后在下次使用的使用就可以直接调用了.本篇所要提到的是JDBC工具类,相信大家在学习java时都接触过.那么对于封装它的方法,本篇先对工具类进行简单的说明,列出有关的封装步骤,然后带来相关的实例. 1.说明 在java开发过程中,代码中时常用到一些Scanner.Random一样的类,他们是键盘录入,生成随机数的类,像一个工具一样,在java中被称为工具类. 2.步骤 封装JDBC工具类 加入获取数据库连接对象的方法 加入释放连接的方法 3

随机推荐