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] " + 58 59 "该剧讲述的是孙悟空、猪八戒、沙僧辅保大唐高僧玄奘去西天取经,师徒四人一路抢滩涉险,降妖伏怪,历经八十一难,取回真经,终修正果的故事。 " + 60 61 "《西游记》于1982年7月3日开机,同年10月1日首播试集《除妖乌鸡国》。1986年春节在央视首播前11集,1988年25集播出。 " + 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集 " + 148 149 "《水浒传》是由中央电视台与中国电视剧制作中心联合出品的43集电视连续剧,根据明代施耐123456789012345678庵的同名小说改编。 [1] 由张绍林执导,杨争光 、冉平改编,李雪健、周野芒、臧金生、丁海峰、赵小锐领衔主演。 " + 150 151 "该剧讲述的是宋朝徽宗时皇帝昏庸、奸臣当道、官府腐败、贪官污吏陷害忠良,弄得民不聊生,许多正直善良的人被官府逼得无路可走,被迫奋起反抗,最终108条好汉聚义梁山泊,但随后宋江对朝廷的投降使得一场轰轰烈烈的农民起义最后走向失败的故事。 [2] " + 152 153 "《水浒传》于1998年1月8日在中央电视台一套首播。 [3] " + 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(" #### 开始组内分页"); 74 75 System.out.println(" 分组名称:" + 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(" #### 开始组内分页"); 30 31 logger.info(" 分组名称:" + groupDocs.groupValue.utf8ToString()); 32 33 ScoreDoc[] scoreDocs = groupDocs.scoreDocs; 34 35 for (ScoreDoc scoreDoc : scoreDocs) { 36 37 logger.info(" 组内记录:" + indexSearcher.doc(scoreDoc.doc)); 38 39 } 40 41 logger.info(" #### 结束组内分页"); 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 }