欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring Boot 整合——MongoDB整合3(MongoDB聚合操作)

程序员文章站 2022-05-01 16:36:08
...

文章前面

关于版本

依赖 版本
springboot 2.0.8.RELEASE
mongodb 4.0.14

本内容只是为了介绍mongodb最基础的使用以及配置,作为一个知名的数据库,其存在相当多的高级用法,展开来介绍内容会相当多,当然本人并非相关领域的大神,下面内容只不过整理了自己日常使用的一些积累。是对自己经验的积累,也希望能帮助后来的同学

关于项目

本内容也是我尝试整理工作中接触过各种工具在springboot中使用的方法。下面介绍的所有方法都已经提供了测试用例。因为每个例子涉及代码较多,所以文章中只贴出了部分代码。全部的代码在这里:https://gitee.com/daifyutils/springboot-samples。

聚合管道操作

MongoTemplate提供了aggregate方法来实现对数据的聚合操作。其核心逻辑在于接受一个TypedAggregation参数,此参数定义了聚合操作的具体限制语句。

	@Override
	public <O> AggregationResults<O> aggregate(TypedAggregation<?> aggregation, Class<O> outputType) {
		return aggregate(aggregation, operations.determineCollectionName(aggregation.getInputType()), outputType);
	}

	/* (non-Javadoc)
	 * @see org.springframework.data.mongodb.core.MongoOperations#aggregate(org.springframework.data.mongodb.core.aggregation.TypedAggregation, java.lang.String, java.lang.Class)
	 */
	@Override
	public <O> AggregationResults<O> aggregate(TypedAggregation<?> aggregation, String inputCollectionName,
			Class<O> outputType) {

		Assert.notNull(aggregation, "Aggregation pipeline must not be null!");

		AggregationOperationContext context = new TypeBasedAggregationOperationContext(aggregation.getInputType(),
				mappingContext, queryMapper);
		return aggregate(aggregation, inputCollectionName, outputType, context);
	}

聚合管道支持操作

MongoTemplate提供的aggregate方法,实际上是mongodb的aggregate()的JAVA版。而实际上MongoTemplate提供的aggregate方法也是生成mongodb的aggregate()的表达式来进行最终的查询。

aggregate你可以认为是一个管道,当MongoDB的文档经过一个管道处理之后处理后的数据会传递给下一个管道来进行处理。这就对管道的使用产生两个影响:1.管道处理是顺序的,相同的管道内容不同的执行顺序会产生不同的结果。2.管道只能处理被之前管道处理过后的数据。

mongodb提供的aggregate()

聚合管道操作

下面是基于聚合管道mongodb提供的可操作的内容

支持的操作 java接口 说明
$project Aggregation.project 修改输入文档的结构。
$match Aggregation.match 用于过滤数据
$limit Aggregation.limit 用来限制MongoDB聚合管道返回的文档数
$skip Aggregation.skip 在聚合管道中跳过指定数量的文档
$unwind Aggregation.unwind 将文档中的某一个数组类型字段拆分成多条
$group Aggregation.group 将集合中的文档分组,可用于统计结果
$sort Aggregation.sort 将输入文档排序后输出
$geoNear Aggregation.geoNear 输出接近某一地理位置的有序文档

聚合内容操作

下面是基于聚合操作(Aggregation.group),mongodb提供可选的表达式

聚合表达式 java接口 说明
$sum Aggregation.group().sum(“field”).as(“sum”) 求和
$avg Aggregation.group().avg(“field”).as(“avg”) 求平均
$min Aggregation.group().min(“field”).as(“min”) 获取集合中所有文档对应值得最小值
$max Aggregation.group().max(“field”).as(“max”) 获取集合中所有文档对应值得最大值
$push Aggregation.group().push(“field”).as(“push”) 在结果文档中插入值到一个数组中
$addToSet Aggregation.group().addToSet(“field”).as(“addToSet”) 在结果文档中插入值到一个数组中,但不创建副本
$first Aggregation.group().first(“field”).as(“first”) 根据资源文档的排序获取第一个文档数据
$last Aggregation.group().last(“field”).as(“last”) 根据资源文档的排序获取最后一个文档数据

聚合管道的实际使用

Group聚合操作

    @Override
    public List<GroupVo> getAggGroup() {
        
        GroupOperation noRepeatGroup = Aggregation.group( "userId","type")
            .count().as("num")
            .sum("totalProduct").as("count");
        TypedAggregation<Order> noRepeatAggregation =
            Aggregation.newAggregation(Order.class,noRepeatGroup);
        AggregationResults<GroupVo> noRepeatDataInfoVos = mongoTemplate.aggregate(noRepeatAggregation, GroupVo.class);
        List<GroupVo> noRepeatDataList = noRepeatDataInfoVos.getMappedResults();
        System.out.println(JSON.toJSONString(noRepeatDataList));
        return noRepeatDataList;
    }

上面例子中对order集合中的userIdtype进行分组,使用count获取总数并映射到num字段中,对totalProduct字段进行求和并映射到count字段中。在所有的Group聚合操作中,除了count之外,其他操作使用格式基本上都是相同的。其在JAVA中的调用都是使用下面格式

GroupOperation noRepeatGroup = Aggregation.group( {分组字段1},{分组字段2})
            .count().as({count结果映射的字段})
            .sum({需要进行sum的字段}).as({sum结果映射的字段})

project操作

    @Override
    public List<GroupVo> getAggProject() {

        // 重排的
        GroupOperation noRepeatGroup = Aggregation.group( "userId","type")
            .count().as("num")
            .sum("totalProduct").as("count");
        Field field = Fields.field("num2", "num");
        ProjectionOperation project = Aggregation.project("userId","type")
            .andInclude(Fields.from(field));

        TypedAggregation<Order> noRepeatAggregation =
            Aggregation.newAggregation(Order.class,noRepeatGroup,project);
        AggregationResults<GroupVo> noRepeatDataInfoVos = mongoTemplate.aggregate(noRepeatAggregation, GroupVo.class);
        List<GroupVo> noRepeatDataList = noRepeatDataInfoVos.getMappedResults();
        System.out.println(JSON.toJSONString(noRepeatDataList));
        return noRepeatDataList;
    }

project主要作用就是调整文档的字段内容。比如上面内容中获取数据条目数最开始映射到num字段中,但是在下面Field field = Fields.field("num2", "num")这里将num的映射转为了num2的映射。这个方法在后续使用lookup进行联表查询的时候会非常有用。因为正常进行联表查询的时候被连接表会作为结果集中的子集出现,而使用project可以将子集数据作为文档的同一层级数据展示出来。

关于match,unwind,sort我会在后面的联表查询、分组去重内容里面来介绍

Bucket操作

在使用聚合管道的时候,可以使用Aggregation.bucket将数据根据某些范围分为多个桶,或者使用Aggregation.bucketAuto将数据分为指定数量的桶。

  1. Aggregation.bucket

Aggregation.bucket方法允许使用者设置一个字段,以及一个多段的查询区间。mongodb将根据这个字段的内容以及设置的分组区间生成多个数据桶。我们可以获取每个桶的聚合结果。下面的例子中就是将Order集合中的数据根据type字段的值分为[0,1),[1,2),[2,3),[3,other)四组。

    @Override
    public List<BucketVo> getAggBucket() {

        BucketOperation bucketOperation =
            // 分组的字段
            Aggregation.bucket("type")
                // 根据范围进行分组
                .withBoundaries(0,1,2,3)
                // 默认分组
                .withDefaultBucket("other")
                // 求总
                .andOutput("_id").count().as("count")
                // 求和
                .andOutput("totalProduct").sum().as("sum")
                // 求平均
                .andOutput("totalMoney").avg().as("avg");
        TypedAggregation<Order> newAggregation =
            Aggregation.newAggregation(Order.class, bucketOperation);

        AggregationResults<BucketVo> noRepeatDataInfoVos2 =
            mongoTemplate.aggregate(newAggregation, BucketVo.class);
        return noRepeatDataInfoVos2.getMappedResults();
    }

字段介绍

  • bucket:需要进行分桶依据的字段
  • withBoundaries:分桶区间的范围内容,需要注意的是这个范围是包含前一个条件的值不包含后一个条件的值
  • withDefaultBucket:所有没被设定范围统计到的数据会被放入到other这个桶中,其中other是这个桶的id
  • andOutput:需要输出的内容,一般是针对某个字段的聚合结果
  1. Aggregation.bucketAuto

bucketAuto是指定的参数和字段,根据对应字段的内容,将数据平均分配为指定数量的桶。比如下面例子中数据根据type字段中的值分成2组

    @Override
    public List<BucketVo> getAggBucketAuto() {
            // 分组的字段
        BucketAutoOperation autoOperation = Aggregation.bucketAuto("type", 2)
            // 求总
            .andOutput("_id").count().as("count")
            // 求和
            .andOutput("totalProduct").sum().as("sum")
            // 求平均
            .andOutput("totalMoney").avg().as("avg");
        TypedAggregation<Order> newAggregation =
            Aggregation.newAggregation(Order.class, autoOperation);

        AggregationResults<BucketVo> noRepeatDataInfoVos2 =
            mongoTemplate.aggregate(newAggregation, BucketVo.class);
        return noRepeatDataInfoVos2.getMappedResults();
    }

字段介绍

  • bucketAuto: 需要设置进行分桶依据的字段,以及需要分的桶数量

  • andOutput:需要输出的内容,一般是针对某个字段的聚合结果


个人水平有限,上面的内容可能存在没有描述清楚或者错误的地方,假如开发同学发现了,请及时告知,我会第一时间修改相关内容,也希望大家看在这个新春佳节只能宅到家中埋头苦逼的码代码的情况下,能给我点一个赞。你的点赞就是我前进的动力。在这里也祝大家新春快乐。