简明:本文详细记录使用Aggregation聚合框架对MongoDB数据库常见操作,包括过滤、分组、求和、排序和分页设计实现等等。相比较于MongoTemplate,使用Aggregation对MongoDB操作更加便捷、方便,特别是分组求和、计算平均值情况等。
    (文章持续更新------后期添加更多基本操作

    (个人记录学习总结内容,若出现错误/改进地方,请指出/分享,共同学习进步!!!)

一、简介

1、聚合简介

          MongoDB中聚合通常用来处理数据,如分组求和、求平均值和排序等,对实现数据复杂操作较为方便,简单来说:聚合就是通过对集合中的数据进行运算,转换为自己需要的形式。
         与上篇文章使用MongoTemplate操作数据相比较,Aggregation聚合操作显得更加有优势和便捷,代码清晰简洁,优化查询语句。

2、聚合管道简介

         简明:在Linux中,管道一般是将当前命令的执行结果作为下个命令执行的参数。

         MongoDB聚合管道:将MongoDB文档在一个管道处理完毕后,将结果传递给下一个管道处理。简单来说:管道就是聚合的整个运算过程。

3、聚合管道常用的表达式(常用)

表达式功能等价SQL
$match过滤数据,输出符合条件文档where
$project修改输入文档结构(筛选展示文档的键)个人理解(select)
$limit限制计算文档结果返回数limit
$sort文档排序order by
$group文档分组group by
$skip跳过指定数量的文档skip
$unwind展开数组(数组内容拆分显示)

二、测试案例

测试数据如下:

 1、实体类

                 简明:为简化代码,已引入Lombok依赖,省略Setter()、Getter()方法

                 简明:@Document注解指定数据存储/操作的集合是PersonCollection

import lombok.Getter;
import lombok.Setter;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

/**
 *  Person 实体类
 *
 *  @Document注解  默认情况下,创建Person集合(可使用@Document注解指定创建集合PersonCollection)
 *
 *  @author LBF
 *  @date 2022/1/18 11:18
 */
@Setter
@Getter
@Document(collection = "PersonCollection")
public class Person {

    /** Id */
    @Id
    private ObjectId id;

    /** 名字 */
    private String name;

    /** 年龄 */
    private Integer age;

    /** 地址 */
    private String addr;

    /** 手机号 */
    private Integer phone;

    /** 类别(测试用) */
    private String type;

    public Person(String name, Integer age, String addr, Integer phone, String type) {
        this.name = name;
        this.age = age;
        this.addr = addr;
        this.phone = phone;
        this.type = type;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", addr='" + addr + '\'' +
                ", phone=" + phone +
                ", type='" + type + '\'' +
                '}';
    }
}

2、测试代码

  注意:测试代码中,键值是指数据库文档的键,而不是指实体类的键值(没有使用反射机制)

(1)常见聚合查询------->match(过滤)、group by(分组)、sum(求和)、sort(排序)

    /**
     *   常见聚合查询 ---------- 1
     *   @return  个人封装的返回体
     */
    @GetMapping("/findDocumentByAgg1")
    public AjaxResult findDocumentByAgg1(){
        //  初始化查询容器(用来构建查询条件)
        Criteria criteria = new Criteria();
        //  设置查询条件:  23  <=  age   <=  30
        criteria.and("age").gte(23).lte(30);

        //  构建聚合操作
        Aggregation aggregation = Aggregation.newAggregation(
                //   使用反射机制,此时的键就是指实体类
                //   Person.class,

                //   利用构建的查询条件进行过滤数据
                Aggregation.match(criteria),
                //   根据 type 键分组  ------>   求和 age 键  -------->  每个分组的求和结果取个别名:totalAge
                Aggregation.group("type").sum("age").as("totalAge"),
                //   将分组求和的结果totalAge,按照降序排序
                Aggregation.sort(Sort.by("totalAge").descending())
        );

        //  将拼接的聚合管道操作传入    
        //  第一个参数:aggregation聚合操作   
        //  第二个参数:操作的集合名称   
        //  第三个参数:输出类型(此处使用Document,包含_id键情况下使用实体类Person会类型转换报错)
        AggregationResults<Document> results =
                mongoTemplate.aggregate(aggregation,"PersonCollection",Document.class);
        return AjaxResult.success(results.getMappedResults());
    }

(2)常见聚合查询------->project(展示键)、skip(跳过)

    @GetMapping("/findDocumentByAgg2")
    public AjaxResult findDocumentByAgg2(){
        //  初始化查询容器(用来构建查询条件)
        Criteria criteria = new Criteria();
        //  设置查询条件:  23  <=  age   <=  30
        criteria.and("age").gte(23).lte(30);

        Aggregation aggregation = Aggregation.newAggregation(
                //  过滤数据
                Aggregation.match(criteria),
                //  选择结果需要展示的字段(默认是全展示,_id键会显示)
                Aggregation.project("name","addr"),
                //  跳过1个结果,显示剩余,即剩余1个结果
                Aggregation.skip(1)
        );

        AggregationResults<Document> results = mongoTemplate.aggregate(aggregation,"PersonCollection",Document.class);
        return AjaxResult.success(results.getMappedResults());
    }

 (3)常见聚合查询------->(分页查询,以查询第2页为例)(首页第1页)

Aggregation聚合分页查询思路: skip跳过2个数据,limit每页显示最大文档数2个,跳过1、2个数据,即显示第3、4个数据,因此就显示第2页。

    /**  Aggregation 分页查询 */
    @GetMapping("/findDocumentByAgg3")
    public AjaxResult findDocumentByAgg3(){

        //  构建聚合操作
        Aggregation aggregation = Aggregation.newAggregation(
                //  展示的键   name   phone  _id键会显示
                Aggregation.project("name","phone"),
                //  跳过某数量的数据,显示剩余(可理解为显示的第几页,如以下:跳过3个,显示第2页)
                //  计算思路:skip((页码-1)* maxElements)
                Aggregation.skip(1*2),
                //  利用limit:限制输出的文档数,即需展示的数据量(可理解为每页显示的数量)
                Aggregation.limit(2)
        );
        AggregationResults<Document> results = mongoTemplate.aggregate(aggregation,"PersonCollection",Document.class);
        return AjaxResult.success(results.getMappedResults());
    }

(4)常见聚合查询------->(持续补充)

 三、总结

         本文记录个人使用Aggregation聚合框架时,一些经常使用到的操作表达式,仍然存在部分地方未能够充分理解进行解释,若文中出现错误/需改进的地方,请指出/分享,共同学习进步!!!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐