1.1. 分组统计

既然是查询,就有可能会用到分组统计。下面介绍一下Lucene的分组统计:

1.1.1. 创建索引

要分组统计,创建索引的时候就要添加一个SortedDocValuesField

  1 /**
  2
  3      * 添加索引
  4
  5      */
  6
  7     @Test
  8
  9     public void addIndex() {
 10
 11 try {
 12
 13 // 获取lucene的写入方法
 14
 15 writer = LuceneUtils.getIndexWriter();
 16
 17
 18
 19 Document doc = new Document();
 20
 21
 22
 23 // 书主键
 24
 25 doc = new Document();
 26
 27     doc.add(new StringField("bookid", "1345678", Field.Store.YES));
 28
 29     // 书名
 30
 31     doc.add(new StringField("bookname", "西游记", Field.Store.YES));
 32
 33     // 书的类型
 34
 35     doc.add(new StringField("booktype", "小说", Field.Store.YES));
 36
 37     // 需要使用特定的field存储分组,需要排序及分组的话,要加上下面语句,注意默认SortedDocValuesField也是不存储的
 38
 39     doc.add(new SortedDocValuesField("booktype", new BytesRef("小说")));
 40
 41     // 书的价格
 42
 43     doc.add(new NumericDocValuesField("bookprice", 123));
 44
 45     // 书的日期年份
 46
 47     Field intPoint = new NumericDocValuesField("bookdate", 1066);
 48
 49     doc.add(intPoint);
 50
 51     intPoint = new StoredField("bookdate", 1066);
 52
 53     doc.add(intPoint);
 54
 55     // 书的内容
 56
 57     doc.add(new TextField("bookcontent", "《西游记》又称央视86版《西游记》,改编自明123456789012345678 12333代小说家吴承恩同名文学古典名著。是由中央电视台、中国电视剧制作中心出品的一部25集古装神话剧。由杨洁执导,戴英禄,杨洁,邹忆青共同编剧,六小龄童、徐少华、迟重瑞、汪粤、马德华、闫怀礼等主演,李世宏、李扬、张云明、里坡等担任主要配音。 [1] \r\n" +
 58
 59      "该剧讲述的是孙悟空、猪八戒、沙僧辅保大唐高僧玄奘去西天取经,师徒四人一路抢滩涉险,降妖伏怪,历经八十一难,取回真经,终修正果的故事。\r\n" +
 60
 61      "《西游记》于1982年7月3日开机,同年10月1日首播试集《除妖乌鸡国》。1986年春节在央视首播前11集,1988年25集播出。\r\n" +
 62
 63      "1986年春节一经播出,轰动全国,老少皆宜,获得了极高评价,造就了89.4%的收视率神话,至今仍是寒暑假被重播最多的电视剧,重播次数超过3000次,依然百看不厌,成为一部公认的无法超越的经典。", Field.Store.YES));
 64
 65     // 添加文档
 66
 67             writer.addDocument(doc);
 68
 69
 70
 71             // 书主键
 72
 73 doc = new Document();
 74
 75     doc.add(new StringField("bookid", "1533278", Field.Store.YES));
 76
 77     // 书名
 78
 79     doc.add(new StringField("bookname", "自然", Field.Store.YES));
 80
 81     // 书的类型
 82
 83     doc.add(new StringField("booktype", "杂志", Field.Store.YES));
 84
 85     // 需要使用特定的field存储分组,需要排序及分组的话,要加上下面语句,注意默认SortedDocValuesField也是不存储的
 86
 87     doc.add(new SortedDocValuesField("booktype", new BytesRef("杂志")));
 88
 89     // 书的价格
 90
 91     doc.add(new NumericDocValuesField("bookprice", 123));
 92
 93     // 书的日期年份
 94
 95     Field intPoint2 = new NumericDocValuesField("bookdate", 1066);
 96
 97     doc.add(intPoint);
 98
 99     intPoint = new StoredField("bookdate", 1066);
100
101     doc.add(intPoint);
102
103     // 书的内容
104
105     doc.add(new TextField("bookcontent", "宣扬科学,发表论文!", Field.Store.YES));
106
107     // 添加文档
108
109          writer.addDocument(doc);
110
111
112
113     // 书主键
114
115   doc = new Document();
116
117       doc.add(new StringField("bookid", "12345678", Field.Store.YES));
118
119       // 书名
120
121       doc.add(new StringField("bookname", "水浒传", Field.Store.YES));
122
123       // 书的类型
124
125       doc.add(new StringField("booktype", "小说", Field.Store.YES));
126
127       // 需要使用特定的field存储分组,需要排序及分组的话,要加上下面语句,注意默认SortedDocValuesField也是不存储的
128
129     doc.add(new SortedDocValuesField("booktype", new BytesRef("小说")));
130
131       // 书的价格
132
133     doc.add(new NumericDocValuesField("bookprice", 123));
134
135     // 书的日期年份
136
137     Field intPoint1 = new NumericDocValuesField("bookdate", 1666);
138
139     doc.add(intPoint1);
140
141     intPoint1 = new StoredField("bookdate", 1666);
142
143     doc.add(intPoint1);
144
145       // 书的内容
146
147       doc.add(new TextField("bookcontent", "中国大陆,中央电视台无锡太湖影视城 43集\r\n" +
148
149        "《水浒传》是由中央电视台与中国电视剧制作中心联合出品的43集电视连续剧,根据明代施耐123456789012345678庵的同名小说改编。 [1]  由张绍林执导,杨争光 、冉平改编,李雪健、周野芒、臧金生、丁海峰、赵小锐领衔主演。\r\n" +
150
151        "该剧讲述的是宋朝徽宗时皇帝昏庸、奸臣当道、官府腐败、贪官污吏陷害忠良,弄得民不聊生,许多正直善良的人被官府逼得无路可走,被迫奋起反抗,最终108条好汉聚义梁山泊,但随后宋江对朝廷的投降使得一场轰轰烈烈的农民起义最后走向失败的故事。 [2] \r\n" +
152
153        "《水浒传》于1998年1月8日在中央电视台一套首播。 [3] \r\n" +
154
155        "2018年9月8日,9月15日,9月22日,央视四台《中国文艺》“向经典致敬”栏目播出《水浒传》20周年聚首专题节目", Field.Store.YES));
156
157
158
159     // 添加文档
160
161             writer.addDocument(doc);
162
163
164
165             // 提交数据,第一次创建的时候需要提交,否则会报错
166
167           long resultcode = writer.commit();
168
169
170
171           if(resultcode > 0l) {
172
173           System.out.println("成功建立索引");
174
175           } else {
176
177           System.out.println("建立索引失败");
178
179           }
180
181 } catch (IOException e) {
182
183 e.printStackTrace();
184
185 }
186
187     }

1.1.2. 分组统计查询

  1 /**
  2  * 分组查询
  3  * @param param
  4  * @param rule
  5  * @return
  6  * @throws IOException
  7  */
  8     @Test
  9 public void GroupingStatistics() throws IOException{
 10
 11 try {
 12 // 获取一个indexReader对象
 13 reader = LuceneUtils.getIndexReader();
 14 // 创建一个indexsearcher对象
 15 searcher = new IndexSearcher(reader);
 16
 17 Query query = new MatchAllDocsQuery();
 18
 19 GroupingUtil groupingUtil = new GroupingUtil();
 20
 21 int curPage = 1;
 22
 23 int pageSize = 10;
 24
 25 // 控制每次返回几组
 26 int groupLimit = curPage*pageSize;
 27 // 控制每一页的组内文档数
 28 int groupDocsLimit = curPage*pageSize;
 29 // 控制组的偏移
 30 int groupOffset = 0;
 31
 32
 33
 34 // 为了排除干扰因素,全部使用默认的排序方式,当然你还可以使用自己喜欢的排序方式
 35         // 初始值为命中的所有文档数,即最坏情况下,一个文档分成一组,那么文档数就是分组的总数
 36         int totalGroupCount = searcher.count(query);
 37         TopGroups<BytesRef> topGroups;
 38
 39         System.out.println("#### 组的分页大小为:" + groupLimit);
 40
 41         System.out.println("#### 组内分页大小为:" + groupDocsLimit);
 42
 43 while (groupOffset < totalGroupCount) {//说明还有不同的分组
 44
 45             // 控制组内偏移,每次开始遍历一个新的分组时候,需要将其归零
 46
 47             int groupDocsOffset = 0;
 48
 49             System.out.println("#### 开始组的分页");
 50
 51             topGroups = groupingUtil.group(searcher, query, "booktype",
 52
 53              groupDocsOffset, groupDocsLimit, groupOffset, groupLimit);
 54
 55             // 具体搜了一次之后,就知道到底有多少组了,更新totalGroupCount为正确的值
 56
 57             totalGroupCount = topGroups.totalGroupCount;
 58
 59             GroupDocs<BytesRef>[] groups = topGroups.groups;
 60
 61             // 开始对组进行遍历
 62
 63             for (int i = 0; i < groups.length; i++) {
 64
 65                 long totalHits = groupingUtil.iterGroupDocs(searcher, groups[i]);// 获得这个组内一共多少doc
 66
 67                 // 获取数据
 68
 69                     TotalHits totalH = groups[i].totalHits;
 70
 71                     totalHits = totalH.value;
 72
 73                     System.out.println("\t#### 开始组内分页");
 74
 75                     System.out.println("\t分组名称:" + groups[i].groupValue.utf8ToString());
 76
 77                     ScoreDoc[] scoreDocs = groups[i].scoreDocs;
 78
 79
 80
 81                     if(scoreDocs!=null && scoreDocs.length>0) {
 82
 83                      for (int j=0; j<1; j++) {
 84
 85              Document document = searcher.doc(scoreDocs[j].doc);
 86
 87              // 打印content字段的值
 88
 89              System.out.println("得分值: "+scoreDocs.length);
 90
 91              System.out.println("bookid: "+document.get("bookid"));
 92
 93              System.out.println("bookname: "+document.get("bookname"));
 94
 95              System.out.println("booktype: "+document.get("booktype"));
 96
 97              System.out.println("bookprice: "+document.get("bookprice"));
 98
 99              System.out.println("bookcontent: "+document.get("bookcontent"));
100
101              }
102
103                     }
104
105                 groupDocsOffset = 0;
106
107             }
108
109             groupOffset += groupLimit;
110
111             System.out.println("#### 结束组的分页");
112
113         }
114
115 } catch (Exception e) {
116
117 e.printStackTrace();
118
119 } finally {
120
121 // 关闭indexReader对象
122
123 reader.close();
124
125 }
126
127 }

结果如下:

#### 组的分页大小为:10

#### 组内分页大小为:10

#### 开始组的分页

2019-11-15 11:43:21,051 INFO  (GroupingUtil.java:30) - #### 开始组内分页

2019-11-15 11:43:21,067 INFO  (GroupingUtil.java:31) - 分组名称:小说

2019-11-15 11:43:21,082 INFO  (GroupingUtil.java:34) - 组内记录:Document<stored,indexed,tokenized,omitNorms,indexOptions=DOCS<bookid:1345678> stored,indexed,tokenized,omitNorms,indexOptions=DOCS<bookname:西游记> stored,indexed,tokenized,omitNorms,indexOptions=DOCS<booktype:小说> stored<bookdate:1066> stored,indexed,tokenized<bookcontent:《西游记》又称央视86版《西游记》,改编自明123456789012345678 12333代小说家吴承恩同名文学古典名著。是由中央电视台、中国电视剧制作中心出品的一部25集古装神话剧。由杨洁执导,戴英禄,杨洁,邹忆青共同编剧,六小龄童、徐少华、迟重瑞、汪粤、马德华、闫怀礼等主演,李世宏、李扬、张云明、里坡等担任主要配音。 [1]

该剧讲述的是孙悟空、猪八戒、沙僧辅保大唐高僧玄奘去西天取经,师徒四人一路抢滩涉险,降妖伏怪,历经八十一难,取回真经,终修正果的故事。

《西游记》于1982年7月3日开机,同年10月1日首播试集《除妖乌鸡国》。1986年春节在央视首播前11集,1988年25集播出。

1986年春节一经播出,轰动全国,老少皆宜,获得了极高评价,造就了89.4%的收视率神话,至今仍是寒暑假被重播最多的电视剧,重播次数超过3000次,依然百看不厌,成为一部公认的无法超越的经典。>>

2019-11-15 11:43:21,082 INFO  (GroupingUtil.java:34) - 组内记录:Document<stored,indexed,tokenized,omitNorms,indexOptions=DOCS<bookid:12345678> stored,indexed,tokenized,omitNorms,indexOptions=DOCS<bookname:水浒传> stored,indexed,tokenized,omitNorms,indexOptions=DOCS<booktype:小说> stored<bookdate:1666> stored,indexed,tokenized<bookcontent:中国大陆,中央电视台无锡太湖影视城 43集

《水浒传》是由中央电视台与中国电视剧制作中心联合出品的43集电视连续剧,根据明代施耐123456789012345678庵的同名小说改编。 [1]  由张绍林执导,杨争光 、冉平改编,李雪健、周野芒、臧金生、丁海峰、赵小锐领衔主演。

该剧讲述的是宋朝徽宗时皇帝昏庸、奸臣当道、官府腐败、贪官污吏陷害忠良,弄得民不聊生,许多正直善良的人被官府逼得无路可走,被迫奋起反抗,最终108条好汉聚义梁山泊,但随后宋江对朝廷的投降使得一场轰轰烈烈的农民起义最后走向失败的故事。 [2]

《水浒传》于1998年1月8日在中央电视台一套首播。 [3]

2018年9月8日,9月15日,9月22日,央视四台《中国文艺》“向经典致敬”栏目播出《水浒传》20周年聚首专题节目>>

2019-11-15 11:43:21,098 INFO  (GroupingUtil.java:36) - #### 结束组内分页

#### 开始组内分页

分组名称:小说

得分值: 2

bookid: 1345678

bookname: 西游记

booktype: 小说

bookprice: null

bookcontent: 《西游记》又称央视86版《西游记》,改编自明123456789012345678 12333代小说家吴承恩同名文学古典名著。是由中央电视台、中国电视剧制作中心出品的一部25集古装神话剧。由杨洁执导,戴英禄,杨洁,邹忆青共同编剧,六小龄童、徐少华、迟重瑞、汪粤、马德华、闫怀礼等主演,李世宏、李扬、张云明、里坡等担任主要配音。 [1]

该剧讲述的是孙悟空、猪八戒、沙僧辅保大唐高僧玄奘去西天取经,师徒四人一路抢滩涉险,降妖伏怪,历经八十一难,取回真经,终修正果的故事。

《西游记》于1982年7月3日开机,同年10月1日首播试集《除妖乌鸡国》。1986年春节在央视首播前11集,1988年25集播出。

1986年春节一经播出,轰动全国,老少皆宜,获得了极高评价,造就了89.4%的收视率神话,至今仍是寒暑假被重播最多的电视剧,重播次数超过3000次,依然百看不厌,成为一部公认的无法超越的经典。

2019-11-15 11:43:21,098 INFO  (GroupingUtil.java:30) - #### 开始组内分页

2019-11-15 11:43:21,098 INFO  (GroupingUtil.java:31) - 分组名称:杂志

2019-11-15 11:43:21,098 INFO  (GroupingUtil.java:34) - 组内记录:Document<stored,indexed,tokenized,omitNorms,indexOptions=DOCS<bookid:1533278> stored,indexed,tokenized,omitNorms,indexOptions=DOCS<bookname:自然> stored,indexed,tokenized,omitNorms,indexOptions=DOCS<booktype:杂志> stored<bookdate:1066> stored,indexed,tokenized<bookcontent:宣扬科学,发表论文!>>

2019-11-15 11:43:21,098 INFO  (GroupingUtil.java:36) - #### 结束组内分页

#### 开始组内分页

分组名称:杂志

得分值: 1

bookid: 1533278

bookname: 自然

booktype: 杂志

bookprice: null

bookcontent: 宣扬科学,发表论文!

#### 结束组的分页

2019-11-15 11:43:21,145 INFO  (LuceneUtils.java:58) - --------Lucene释放关闭资源中....

2019-11-15 11:43:21,145 INFO  (LuceneUtils.java:76) - --------Lucene释放关闭资源成功....

1.1.3. GroupingUtil

  1 import java.io.IOException;
  2 import org.apache.lucene.search.IndexSearcher;
  3 import org.apache.lucene.search.Query;
  4 import org.apache.lucene.search.ScoreDoc;
  5 import org.apache.lucene.search.Sort;
  6 import org.apache.lucene.search.TotalHits;
  7 import org.apache.lucene.search.grouping.GroupDocs;
  8 import org.apache.lucene.search.grouping.GroupingSearch;
  9 import org.apache.lucene.search.grouping.TopGroups;
 10 import org.apache.lucene.util.BytesRef;
 11 import org.slf4j.Logger;
 12 import org.slf4j.LoggerFactory;
 13 /**
 14  * Lucene8
 15  * 分组统计查询
 16  * @author limingcheng
 17  *
 18  */
 19 public class GroupingUtil {
 20
 21 private static final Logger logger = LoggerFactory.getLogger(GroupingUtil.class);
 22
 23     public long iterGroupDocs(IndexSearcher indexSearcher, GroupDocs<BytesRef> groupDocs) throws IOException {
 24
 25         TotalHits totalHits = groupDocs.totalHits;
 26
 27         long rsvalue = totalHits.value;
 28
 29         logger.info("\t#### 开始组内分页");
 30
 31         logger.info("\t分组名称:" + groupDocs.groupValue.utf8ToString());
 32
 33         ScoreDoc[] scoreDocs = groupDocs.scoreDocs;
 34
 35         for (ScoreDoc scoreDoc : scoreDocs) {
 36
 37          logger.info("\t\t组内记录:" + indexSearcher.doc(scoreDoc.doc));
 38
 39         }
 40
 41         logger.info("\t#### 结束组内分页");
 42
 43         return rsvalue;
 44
 45     }
 46
 47
 48
 49     public TopGroups<BytesRef> group(IndexSearcher indexSearcher, Query query, String groupField,
 50
 51                                      int groupDocsOffset, int groupDocsLimit, int groupOffset, int groupLimit) throws Exception {
 52
 53         return group(indexSearcher, query, Sort.RELEVANCE, Sort.RELEVANCE, groupField, groupDocsOffset, groupDocsLimit, groupOffset, groupLimit);
 54
 55     }
 56
 57
 58     /**
 59
 60      * 分组统计查询
 61
 62      * @param indexSearcher
 63
 64      * @param query
 65
 66      * @param groupSort
 67
 68      * @param withinGroupSort
 69
 70      * @param groupField
 71
 72      * @param groupDocsOffset
 73
 74      * @param groupDocsLimit
 75
 76      * @param groupOffset
 77
 78      * @param groupLimit
 79
 80      * @return
 81
 82      * @throws Exception
 83
 84      */
 85
 86     public TopGroups<BytesRef> group(IndexSearcher indexSearcher, Query query, Sort groupSort, Sort withinGroupSort,
 87
 88      String groupField, int groupDocsOffset, int groupDocsLimit, int groupOffset, int groupLimit)
 89
 90      throws Exception {
 91
 92         //实例化GroupingSearch实例,传入分组域
 93
 94         GroupingSearch groupingSearch = new GroupingSearch(groupField);
 95
 96         //设置组间排序方式
 97
 98         groupingSearch.setGroupSort(groupSort);
 99
100         //设置组内排序方式
101
102         groupingSearch.setSortWithinGroup(withinGroupSort);
103
104         //是否要填充每个返回的group和groups docs的排序field
105
106 //        groupingSearch.setFillSortFields(true);
107
108         //设置用来缓存第二阶段搜索的最大内存,单位MB,第二个参数表示是否缓存评分
109
110         groupingSearch.setCachingInMB(64.0, true);
111
112         //是否计算符合查询条件的所有组
113
114         groupingSearch.setAllGroups(true);
115
116         groupingSearch.setAllGroupHeads(true);
117
118         //设置一个分组内的上限
119
120         groupingSearch.setGroupDocsLimit(groupDocsLimit);
121
122         //设置一个分组内的偏移
123
124         groupingSearch.setGroupDocsOffset(groupDocsOffset);
125
126         TopGroups<BytesRef> result = groupingSearch.search(indexSearcher, query, groupOffset, groupLimit);
127
128         return result;
129
130     }
131
132 }
12-31 18:34