1 简介

在工作的过程中,使用MongoDB存储业务数据,有时候想做的不仅仅是将数据从MongoDB中提取出来,可能需要对数据进行分析并加以利用。在党费收缴模块开发过程中,需要根据党支部名称进行分组,统计出党支部内人员的数目,每个党支部应缴党费和实缴党费的总和 等统计信息。
由于spring-data-mongodb-1.9.5.RELEASE版本太低,因此在调用mongoTemplate类型的aggregate聚合框架时,曝出如下问题

The 'cursor' option is required, except for aggregate with the explain argument

花费了较多的时间,最后选择不使用聚合框架aggregate,改用了如下函数实现了该功能

public DBObject group(DBObject key, DBObject cond, DBObject initial, String reduce) {return this.group(key, cond, initial, reduce, (String)null);
}

2 问题描述

2.1 数据库表

 // 21
{"_id": "5283e22c98dd4158a2bf378fb394ad7b","_class": "DBObject","name": "partydues","cname": "党费收缴","map": {"partyDues": 410,"assigneeXM": "霍建华","year": "2019","assigneeUserID": "8ef3f163242b4059a4187f3eaf07f7e1","roleId": "009002","dataType": "task","assigneeUserName": "huojianhua","pid": "3312512","userId": "3e7b5e3d9b6f4c768524453115baca81","partyDueShouldBe": 95.06,"cnName": "周杰伦","month": "01","createTime": "2018-11-27 16:28:41","roleName": "党支部1","taskId": "3312578","username": "gaochao"}
}// 22
{"_id": "9513e93b58534bbb8441cca2edabef25","_class": "DBObject","name": "partydues","cname": "党费收缴","map": {"partyDues": 711,"assigneeXM": "宋全恒","year": "2019","assigneeUserID": "cf8d1fd5c23f4778b7c0f537d6de2c41","roleId": "009003","dataType": "task","assigneeUserName": "songquanheng","pid": "3312512","userId": "be2c971ee03b4a4ea2577f381329818f","partyDueShouldBe": 95.06,"cnName": "沈万三","month": "01","createTime": "2018-11-27 16:29:30","roleName": "党支部2","taskId": "3312562","username": "shenwanshan"}
}// 23
{"_id": "470e1e258b0443d2aa4b1c6b3cce5561","_class": "DBObject","name": "partydues","cname": "党费收缴","map": {"partyDues": 100,"assigneeXM": "宋全恒","year": "2019","assigneeUserID": "cf8d1fd5c23f4778b7c0f537d6de2c41","roleId": "009003","dataType": "task","assigneeUserName": "songquanheng","pid": "3325001","userId": "be2c971ee03b4a4ea2577f381329818f","partyDueShouldBe": 95.06,"cnName": "周芷若","month": "02","createTime": "2018-11-27 17:09:00","roleName": "党支部1","taskId": "3325051","username": "zhouzhiruo"}
}

2.2 shell命令

db.partyDues.aggregate([$match: {“map.month”: “11”, “map.year”: “2018”, “map.dataType”: “task”}, {“$group”: {_id: “$map.roleName”,“sumOfPartyDueShouldBe”: {“$sum”: “$map.partyDueShouldBe”},“sumOfPartyDues”: {“$sum”: “$map.partyDues”},“totalNumOfPerson”: {“$sum”: 1}}
}
])

2.3 Java源代码实现

相应的Java的源代码如下:

public JSONObject findPartyDuesByParty(String year, String month) {String reduce = "function(doc, pre) " +"{" +"pre.count+=1;" +"var partyDueShouldBe = doc.map.partyDueShouldBe;"+"partyDueShouldBe = typeof(partyDueShouldBe)=='string'?0:partyDueShouldBe;"+"pre.sumOfPartyDueShouldBe+=partyDueShouldBe;" +"pre.sumOfPartyDues += doc.map.partyDues;" +"}";Query query = new Query();query.addCriteria(new Criteria("map.year").is(year)).addCriteria(new Criteria("map.month").is(month)).addCriteria(new Criteria("map.dataType").is("task"));DBObject result = mongoTemplate.getCollection(PARTYDUES_TABLE).group(new BasicDBObject("map.roleName", 1).append("map.roleId", 0),query.getQueryObject(), new BasicDBObject("count", 0).append("sumOfPartyDueShouldBe", 0).append("sumOfPartyDues", 0),  reduce);if (null == result) {logger.info("findPartyDuesByParty cannot find partyDues Info using the reduce");return null;}JSONObject ret= JSONObject.fromObject(result.toMap());DecimalFormat df = new DecimalFormat("0.00");JSONObject res = new JSONObject();for (int i=0; i<ret.size(); i++) {JSONObject ele = ret.getJSONObject(String.valueOf(i));String key = ele.getString("map.roleName");double sumOfPartyDueShouldBe = ele.getDouble("sumOfPartyDueShouldBe");double sumOfPartyDues = ele.getDouble("sumOfPartyDues");sumOfPartyDueShouldBe = Double.parseDouble(df.format(sumOfPartyDueShouldBe));sumOfPartyDues = Double.parseDouble(df.format(sumOfPartyDues));//获取党费收缴负责人信息String roleId = ele.getString("map.roleId");Map<String, String> leaderMap = null;try {leaderMap = userService.getPartyLeaderInfoByRoleId(roleId);} catch (Exception e) {e.printStackTrace();}if (null == leaderMap) {logger.info("Program cannot find the leaderUserId using roleId: "+ roleId);return null;}String leaderXM = leaderMap.get("XM");String leaderPhone = leaderMap.get("PHONE");ele.put("sumOfPartyDueShouldBe", sumOfPartyDueShouldBe);ele.put("sumOfPartyDues", sumOfPartyDues);ele.put("year", year);ele.put("month", month);ele.put("partyLeaderName", leaderXM);ele.put("partyLeaderPhone", leaderPhone);res.put(key, ele);}return res;}

2.4 Group操作

作为使用Map-Reduce的替代方法进行数据聚合,程序员可以使用group操作,这个操作与SQL的group在查询格式上类似,因此在使用上更加方便。但使用group操作确实有一些限制,举例来说它不可使用在分享环境shared environment。并且以BSON格式返回完整的结果集,因此结果应该比较小,少于10000keys。

{ "_id" : ObjectId("4ec1d25d41421e2015da64f1"), "x" : 1 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f2"), "x" : 1 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f3"), "x" : 2 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f4"), "x" : 3 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f5"), "x" : 3 }
{ "_id" : ObjectId("4ec1d25d41421e2015da64f6"), "x" : 3 }

如果要以每行记录中的x进行分组,同时统计出每个x出现的次数。则对应的java代码如下:

GroupByResults<XObject> results =
mongoTemplate.group("group_test_collection",                                                      GroupBy.key("x").initialDocument("{ count: 0 }").reduceFunction("function(doc, prev) { prev.count += 1 }"), XObject.class);

第一个参数为要进行聚合的集合名,第二个参数则是流式API,通过GroupBy类指定了group操作的属性。在例子中仅仅使用了initialDocument和reduceFuction方法。我们也可以指定一个key-function,以及一个finalizer作为流式 API一部分。如果你有多个keys要进行分组,则可以传递多个keys列表,以逗号分隔。
group操作的原生结果是一个JSON文档,看起来如下:

{"retval" : [ { "x" : 1.0 , "count" : 2.0} ,{ "x" : 2.0 , "count" : 1.0} ,{ "x" : 3.0 , "count" : 3.0} ] ,"count" : 6.0 ,"keys" : 3 ,"ok" : 1.0
}

在retval文档被映射到group方法中第三个参数,在这个情形下XObject类展示如下:

public class XObject {
private float x;
private float count;public float getX() {
return x;}public void setX(float x) {
this.x = x;}public float getCount() {
return count;}public void setCount(float count) {
this.count = count;}@Override
public String toString() {
return “XObject [x=” + x + “ count = “ + count + “]”;}
}

也可以通过调用getRawResults在类GroupByResults类上获得原生结果成一个Document。

3 参考

https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.aggregation

4文档下载

https://download.csdn.net/download/lk142500/10815398

mongoTemplate使用group函数进行分组和统计相关推荐

  1. spring-mongoDB mongoTemplate排序分组|分页|统计

    包含一些mongo的常用操作:统计求和,常规排序,分页,查询子文档,查询子文档的数组,排序后分组,按条件删除等 统计求和转换字符串为double db.getCollection('receipt_i ...

  2. pandas使用groupby函数和count函数返回的是分组下每一列的统计值(不统计NaN缺失值)、如果多于一列返回dataframe、size函数返回分组下的行数结果为Series(缺失值不敏感)

    pandas使用groupby函数和count函数返回的是分组下每一列的统计值(不统计NaN缺失值).如果多于一列返回dataframe.size函数返回分组下的行数结果为Series(不区分缺失值和 ...

  3. pandas使用groupby函数进行分组聚合、使用agg函数指定聚合统计计算的数值变量、并自定义统计计算结果的名称(naming columns after aggregation)

    pandas使用groupby函数进行分组聚合.使用agg函数指定聚合统计计算的数值变量.并自定义统计计算结果的名称(naming columns after aggregation in dataf ...

  4. pandas使用groupby函数按照多个分组变量进行分组聚合统计、使用agg函数计算分组的多个统计指标(grouping by multiple columns in dataframe)

    pandas使用groupby函数按照多个分组变量进行分组聚合统计.使用agg函数计算分组的多个统计指标(grouping by multiple columns in dataframe) 目录

  5. R语言ggpubr包ggsummarystats函数可视化分组条形图(自定义分组颜色、添加抖动数据点jitter、误差条)并在X轴标签下方添加分组对应的统计值(样本数N、中位数、四分位数的间距iqr)

    R语言ggpubr包ggsummarystats函数可视化分组条形图(自定义分组颜色.添加抖动数据点jitter.误差条error bar)并在X轴标签下方添加分组对应的统计值(样本数N.中位数med ...

  6. R语言编写自定义函数基于ggsumarystats函数计算每个分组的统计值、自定义可视化分组分面条形图,并在X轴标签下方添加分组对应的统计值(样本数N、中位数median、四分位数的间距iqr)

    R语言编写自定义函数基于ggsumarystats函数计算每个分组的统计值.自定义可视化分组分面条形图,并在X轴标签下方添加分组对应的统计值(样本数N.中位数median.四分位数的间距iqr) 目录

  7. SQL进行排序、分组、统计的10个新技巧

    1.使用排序使数据有序 通常,你的所有数据真正需要的仅仅是按某种顺序排列.SQL的ORDER BY语句可以以字母或数字顺序组织数据.因此,相似的值按组排序在一起.然而,这个分组时排序的结果,并不是真的 ...

  8. SQL语句汇总(三)——聚合函数、分组、子查询及组合查询

    聚合函数: SQL中提供的聚合函数可以用来统计.求和.求最值等等. 分类: –COUNT:统计行数量 –SUM:获取单个列的合计值 –AVG:计算某个列的平均值 –MAX:计算列的最大值 –MIN:计 ...

  9. 【MySQL基础攻难1】聚集函数和分组查询

    文章目录 1.数据准备 2.聚集函数 3.分组查询 3.0分组查询基本语法 3.1 SELECT后面语句的要求 3.2.GROUP BY到底怎么分组的 3.3.WHERE 和HAVING的区别 3.4 ...

最新文章

  1. Pheatmap热图的绘制及如何调整图片
  2. python解析库_Python命令行解析库argparse
  3. psp能装安卓软件吗_王思聪:翻译软件能翻译出文化吗?
  4. Qt 实现数据协议控制--组帧、组包、解析帧、解析包
  5. ListBox的使用
  6. php接口异常,api接口异常怎么办
  7. epoll为什么比select和poll效率更高
  8. latex 符号_sympy: 符号运算-1
  9. 【Java】如何较好地将int转化成String
  10. 从事前端多年,我是这样看待三大框架的
  11. NeHe OpenGL第三十二课:拾取游戏
  12. dos2unix命令
  13. c语言文学研究助手报告,文学研究助手数据结构报告
  14. Uncode、ASCII、UTF-8之前的转换函数
  15. 制表符输出语法分析器的格式
  16. 运用三角不等式加速Kmeans聚类算法
  17. Name was not previously introduced as per JSP.5.3的解决办法
  18. 使用音频分析工具audacity分析wave文件
  19. matlab输出的图怎么设置网格背景图片,4.11Python数据处理篇之Matplotlib系列(十一)---图例,网格,背景的设置...
  20. 异地北京办理居住证详细材料

热门文章

  1. SDL2.0--绘制文字
  2. 在 Windows 下查看开放端口
  3. 【HTTP协议】HTTP请求/响应报文结构
  4. 磁盘清理工具(python)
  5. 修改网站地址栏小图标
  6. 闭环频率特性与时域性能指标之间的关系
  7. android 录音的格式,Android录音mp3格式实例详解
  8. 奇葩算法系列——睡眠排序 (面条排序)
  9. DIN:使用Attention挖掘历史数据的深度兴趣网络
  10. 《图解HTTP》读感