Lucene学习:分组统计

Posted bestlmc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lucene学习:分组统计相关的知识,希望对你有一定的参考价值。

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 }

 

以上是关于Lucene学习:分组统计的主要内容,如果未能解决你的问题,请参考以下文章

Javalucene4.0学习心得

简单的sql分组统计

零基础带你学习MySQL—分组统计

solr 学习片段

oracle学习篇五:组函数,分组统计

Shell学习笔记:awk实现group by分组统计功能