目录
一、前言
聚合操作组值来自多个文档,可以对分组数据执行各种操作以返回单个结果。聚合操作包含三类:单一作用聚合、聚合管道、MapReduce。
- 单一作用聚合:提供对常见聚合过程的简单访问,操作都从单个集合聚合文档
- 聚合管道操作:将文档在一个管道处理完毕后,把处理的结果传递给下一个管道进行再次处理
- MapReduce操作:是将集合中的批量文档进行分解处理,然后将处理后的各个结果进行合并输出
二、聚合管道操作
管道的主要函数方法如下:
java
名称 描述 类比sql
$avg 计算均值 avg
$first 返回每组第一个文档,如果有排序,按照排序,如果没有按照默认的存储的顺序返回第一个文档。 limit 0,1
$last 返回每组最后一个文档,如果有排序,按照排序,如果没有按照默认的存储的顺序返回最后一个文档。 -
$max 根据分组,获取集合中所有文档对应值的最大值。 max
$min 根据分组,获取集合中所有文档对应值的最小值。 min
$push 将指定的表达式的值添加到一个数组中。 -
$addToSet 将表达式的值添加到一个集合中(无重复值,无序)。 -
$sum 计算总和 sum
$stdDevPop 返回输入值的总体标准偏差(population standard deviation) -
$stdDevSamp 返回输入值的样本标准偏差(the sample standard deviation) -
2.1、实际案例1
classDo是班级信息
StudentDo是学生信息
teacherDo是老师信息
一个学生 关联一个班级;一个班级有多个学生;
一个老师挂在多个班级下;一个班级拥有多个老师;
主要表结构字段:
java
ClassDo班级信息
@JSONField(name = "class_no")
private String classNo;
@JSONField(name = "class_name")
private String className;
@JSONField(name = "class_position")
private String classPosition;
List<StudentDo> studentDos; //关联学生们
List<TeacherDo> teacherDos; //关联老师们
StudentDo学生信息
@JSONField(name = "stu_id")
private String stuId;
@JSONField(name = "stu_name")
private String stuName;
@JSONField(name = "stu_age")
private String stuAge;
@JSONField(name = "chinese_score")
private String chineseScore;
@JSONField(name = "match_score")
private String matchScore;
@JSONField(name = "class_id")
private ObjectId classId; //学生属于哪个班级
ClassDo classDo; //学生属于具体哪个班
TeacherDo老师信息
@JSONField(name = "te_id")
private String teId;
@JSONField(name = "te_name")
private String teName;
@JSONField(name = "te_age")
private String teAge;
@JSONField(name = "class_ids")
private List<ObjectId> classIds; //老师教学多个班级
(1)、案例--根据学生no,找到对应班级名称
java
public void aggregateStudent(String stuName,String stuAge){
int page =2,maxElements=10;
Criteria criteria = new Criteria();
criteria.and("stuName").is(stuName);
criteria.and("stuAge").is(stuAge);
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.lookup("w_class","classId","_id","classDo"),
// Aggregation.unwind("classDo") //加上这行,那么classDo关联时,classDo节点为null,那么就去去掉这一行
Aggregation.unwind("classDo",true), //classDo为null,这个数组[index]还存在,但classDo字段不存在了
// 计算思路:skip((页码-1)* maxElements)
Aggregation.sort(Sort.by("stu_age").descending()),
Aggregation.skip((page-1)*maxElements),
// 利用limit:限制输出的文档数,即需展示的数据量(可理解为每页显示的数量)
Aggregation.limit(maxElements)
);
/**
* aggregate()表明 主要查询w_student表,对应的查询条件是match(criteria);
* 接着要关联w_class表。 w_student表的class_id字段关联w_class表的_id字段。
*/
AggregationResults<JSONObject> aggregationResults = mongoTemplate.aggregate(aggregation, "w_student", JSONObject.class);
List<JSONObject> infoList = aggregationResults.getMappedResults();
log.info("aggregationResults:{}", JSON.toJSONString(infoList));
}
(2)、案例--这个班级有哪些学生和哪些老师在任课
备注:主表有查询条件;关联两个子表;以主表返回结果
java
public void aggregateClass(String className){
Criteria criteria = new Criteria();
criteria.and("className").is(className);
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.lookup("w_student","_id","classId","studentDos"),
Aggregation.lookup("w_teacher","_id","classIds","teacherDos")
);
AggregationResults<JSONObject> aggregationResults = mongoTemplate.aggregate(aggregation, "w_class", JSONObject.class);
List<JSONObject> infoList = aggregationResults.getMappedResults();
log.info("aggregationResults:{}", JSON.toJSONString(infoList));
}
2.2、实际案例2
现有两个mongodb表,business_permission存储某个业务的权限信息;business_a表是具体某个业务;其中business_a表的主键id关联到business_permission表的business_id。
(1)、案例--主表和关联表都有条件限制,且分页返回
由于这里的查询要求分页,那么只能对要分页的主表进行返回。
java
public Object templateAggregation2(int page,int size){
//主表的查询条件
Criteria criteria = new Criteria();
criteria.and("state").is(Boolean.TRUE);
//关联表的查询条件
Criteria permissionCri = new Criteria();
permissionCri.and("templateInfo.business_type").is(7);
//
/**
templateInfo是关联的表结构;这个结构的permission节点是一个数组,在具体按照要求进行过滤得到需要的权限是否存在
*/
Criteria businessCriteria = new Criteria(); businessCriteria.andOperator(Criteria.where("templateInfo.permission.id").is("04"),Criteria.where("templateInfo.permission.permission_type").is(2));
Criteria businessCriteria2 = new Criteria();
businessCriteria2.andOperator(Criteria.where("templateInfo.permission.id").is("01"),Criteria.where("templateInfo.permission.permission_type").is(2));
List<Criteria> busCriteriaList = new ArrayList<>();
busCriteriaList.add(businessCriteria);
busCriteriaList.add(businessCriteria2);
busCriteriaList.add(Criteria.where("templateInfo.all_flag").is(Boolean.TRUE));
busCriteriaList.add(Criteria.where("templateInfo.permission").is(null));
busCriteriaList.add(Criteria.where("templateInfo.permission").is(new ArrayList<>()));
Criteria[] criArray = new Criteria[busCriteriaList.size()];
busCriteriaList.toArray(criArray);
permissionCri.orOperator(criArray);
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria), //主mongodb自身查询条件
Aggregation.lookup("business_permission","_id","business_id","templateInfo"), //business_permission的business_id对应主表的id
Aggregation.match(permissionCri), //关联表查询条件
Aggregation.sort(new Sort(Sort.Direction.DESC, "update_time")),
// 计算思路:skip((页码-1)* maxElements)
Aggregation.skip((page-1)*size),
Aggregation.limit(size) //这分页的
// Aggregation.group("template_type") //按照分组返回特定的字段值 【可以统计数量】
// .sum("id").as("sumValue") //对id进行求和
// .first("template_code").as("templateCode")
// .avg("doc_view_number").as("avgValue")
);
AggregationResults<JSONObject> aggregationResults = mongoTemplate.aggregate(aggregation, "business_a", JSONObject.class);
List<JSONObject> templateList = aggregationResults.getMappedResults();
log.info("templateList All:{}", JSON.toJSONString(templateList));
return templateList;
}