MongoTemplate 教程系列(三)

第三章:Aggregate

在上一篇文章,讲了spring-data-mongodb常用的增删改查(CRUD)操作,但是平时我们除了这些简单的操作外还需要进行一些复杂统计。本章就介绍mongodb 强大的聚合操作“Aggregate”。

一、Aggregate

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。

mongodb语法

db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
复制代码

mongoTemplate中定义的相关方法:

<O> AggregationResults<O> aggregate(Aggregation aggregation, Class<?> inputType, Class<O> outputType)

<O> AggregationResults<O> aggregate(Aggregation aggregation, String collectionName, Class<O> outputType)

<O> AggregationResults<O> aggregate(TypedAggregation<?> aggregation, Class<O> outputType)

<O> AggregationResults<O> aggregate(TypedAggregation<?> aggregation, String inputCollectionName, Class<O> outputType)

<T> ExecutableAggregationOperation.ExecutableAggregation<T> aggregateAndReturn(Class<T> domainType)

<O> CloseableIterator<O> aggregateStream(Aggregation aggregation, Class<?> inputType, Class<O> outputType)

<O> CloseableIterator<O> aggregateStream(Aggregation aggregation, String collectionName, Class<O> outputType)

<O> CloseableIterator<O> aggregateStream(TypedAggregation<?> aggregation, Class<O> outputType)

<O> CloseableIterator<O> aggregateStream(TypedAggregation<?> aggregation, String inputCollectionName, Class<O> outputType)
复制代码

二、Example

这部分结合需求来阐述:

  • 比如数据库中存储的数据如下:

  • 需求

    Q1.统计各个年级人数

    Q2.统计某个年级某一项测试在某个范围的人数

    Q3.统计某个年级某一项测试不在某个范围的人数

    Q4.统计各个测试项目得分的最大值,最小值,平均值

    Q5.统计各个年级每一个项目得分的最大值,最小值,平均值

  • 代码实现

    • Q1:"统计各个年级人数" 这个比较简单,我们只需要按照年级分组然后进行sum就能得到结果

      mongo:

      db.pt.aggregate([ { "$group" : { "_id" : "$grade_name" , "总人数" : { "$sum" : 1}}}] )
      复制代码

      java:

      Aggregation aggregation1 = Aggregation.newAggregation(Aggregation.group("grade_name").count().as("总人数"));
      AggregationResults<BasicDBObject> outputTypeCount1 =
              mongoTemplate.aggregate(aggregation1, "pt", BasicDBObject.class);
      
      for (Iterator<BasicDBObject> iterator = outputTypeCount1.iterator(); iterator.hasNext(); ) {
          DBObject obj = iterator.next();
          System.out.println(JSON.toJSONString(obj));
      }
      复制代码

      结果:

      mongo-vue中:

      console中:


    • Q2:"统计某个年级某一项测试在某个范围的人数" 这个也不难,只需要匹配 年级+测试项目+项目分数 between 分数1 and 分数2 然后根据年级分组统计

      mongo:

      db.pt.aggregate(
              [ { "$match" : { "grade_name" : "一年级"}} , { "$unwind" : "$items"} , { "$match" : { "items.item_name" : "BMI" , "items.score" : { "$gt" : 60 , "$lt" : 70}}} , { "$group" : { "_id" : "$grade_name" , "一年级BMI正常人数" : { "$sum" : 1}}}]
      )
      复制代码

      java:

      Aggregation aggregation4 =
              Aggregation.newAggregation(
                      Aggregation.unwind("items"),
                      Aggregation.match(Criteria.where("items.item_name").is("BMI").and("items.score").gt(60).lt(70)),
                      Aggregation.group("grade_name").count().as("BMI正常人数"));
      AggregationResults<BasicDBObject> outputTypeCount4 =
              mongoTemplate.aggregate(aggregation4, "pt", BasicDBObject.class);
      
      for (Iterator<BasicDBObject> iterator = outputTypeCount4.iterator(); iterator.hasNext(); ) {
          DBObject obj = iterator.next();
          System.out.println(JSON.toJSONString(obj));
      }
      复制代码

      结果:

      mongo-vue中:

      console中:


    • Q3:"统计某个年级某一项测试不在某个范围的人数" 这个和Q2的区别在于他是not between and,这我们需要使用or来出来,java中对应的是orOperator;

      mongo:

      db.pt.aggregate(
              [ { "$match" : { "grade_name" : "一年级"}} , { "$unwind" : "$items"} , { "$match" : { "items.item_name" : "BMI" , "$or" : [ { "items.score" : { "$lte" : 60}} , { "items.score" : { "$gte" : 70}}]}} , { "$group" : { "_id" : "$grade_name" , "BMI不正常人数" : { "$sum" : 1}}}]
      )
      复制代码

      java:

      Aggregation aggregation3 =
              Aggregation.newAggregation(
                      Aggregation.match(Criteria.where("grade_name").is("一年级")),
                      Aggregation.unwind("items"),
                      Aggregation.match(Criteria.where("items.item_name").is("BMI").orOperator(
                              Criteria.where("items.score").lte(60),
                              Criteria.where("items.score").gte(70))),
                      Aggregation.group("grade_name").count().as("BMI不正常人数"));
      AggregationResults<BasicDBObject> outputTypeCount3 =
              mongoTemplate.aggregate(aggregation3, "pt", BasicDBObject.class);
      
      for (Iterator<BasicDBObject> iterator = outputTypeCount3.iterator(); iterator.hasNext(); ) {
          DBObject obj = iterator.next();
          System.out.println(JSON.toJSONString(obj));
      }
      复制代码

      这里有个问题需要注意:当定义多个Criteria (criteria1,criteria2,criteria3,criteria4)然后使用orOperator拼接的时候结果是不正确的,我实验然后错误的写法有两种:

      1. criteria1.orOperator(criteria2) //criteria1 criteria2 是并列条件满足任一即可的【这个写法是铁定错误的!!!】
      
      2. new Criteria().orOperator(criteria1,criteria2)//criteria1 criteria2 是并列条件满足任一即可的【这个写法百度说是正确写法,不知道为什么结果和上面的是一样的】
      复制代码

      结果:

      mongo-vue中:

      console中:


    • Q4.“统计各个测试项目得分的最大值,最小值,平均值” 这个问题其实就是针对测试项目进行分组,然后使用 min max avg函数

      mongo:

      db.pt.aggregate(
              [ { "$match" : { "grade_name" : "一年级"}} , { "$unwind" : "$items"} , { "$group" : { "_id" : "$items.item_name" , "平均分" : { "$avg" : "$items.score"} , "最小值" : { "$min" : "$items.score"} , "最大值" : { "$max" : "$items.score"}}}]
      )
      复制代码

      java:

      Aggregation aggregation5 =
              Aggregation.newAggregation(
                      Aggregation.match(Criteria.where("grade_name").is("一年级")),
                      Aggregation.unwind("items"),
                      Aggregation.group("$items.item_name").avg("$items.score").as("平均分").min("$items.score").as
                              ("最小值").max("$items.score").as("最大值"));
      AggregationResults<BasicDBObject> outputTypeCount5 =
              mongoTemplate.aggregate(aggregation5, "pt", BasicDBObject.class);
      
      for (Iterator<BasicDBObject> iterator = outputTypeCount5.iterator(); iterator.hasNext(); ) {
          DBObject obj = iterator.next();
          System.out.println(JSON.toJSONString(obj));
      }
      复制代码

      使用原生语句的写法:

      //展开数组
      DBObject queryUnwind=new BasicDBObject("$unwind","$items");
      //分组统计
      DBObject groupObject=new BasicDBObject("_id",new BasicDBObject("item_name", "$items.item_name"));
      groupObject.put("min", new BasicDBObject("$min","$items.score"));
      groupObject.put("max", new BasicDBObject("$max","$items.score"));
      groupObject.put("avg", new BasicDBObject("$avg","$items.score"));
      DBObject  queryGroup=new BasicDBObject("$group",groupObject);
      
      AggregationOutput output=mongoTemplate.getCollection("pt").aggregate(queryUnwind,queryGroup);
      for (Iterator<DBObject> iterator = output.results().iterator(); iterator.hasNext();) {
          DBObject obj =iterator.next();
          System.out.println(obj.toString());
      }
      复制代码

      结果:

      mongo-vue中:

      console中:


    • Q5 .“统计各个年级每一个项目得分的最大值,最小值,平均值” 这个问题需要我们按照年级和项目名分组 计算出每一个的最大值,最小值,平均值然后把每个年级的push到一起

      这里我直接使用类似原生语句的写法,不在单独写出mongo语句怎么写。

      java:

      /* 创建 $unwind 操作, 用于切分数组*/
      DBObject unwind = new BasicDBObject("$unwind", "$items");
      
      /* Group操作*/
      DBObject groupFields = new BasicDBObject("_id",new BasicDBObject("grade_name", "$grade_name").append("item_name", "$items.item_name"));
      groupFields.put("min_score", new BasicDBObject("$min","$items.score"));
      groupFields.put("max_score", new BasicDBObject("$max","$items.score"));
      groupFields.put("avg_score", new BasicDBObject("$avg","$items.score"));
      DBObject group = new BasicDBObject("$group", groupFields);
      
      /* Reshape Group Result*/
      DBObject projectFields = new BasicDBObject();
      projectFields.put("grade_name", "$_id.grade_name");
      DBObject subProjects=new BasicDBObject("item_name","$_id.item_name");
      subProjects.put("min","$min_score");
      subProjects.put("max","$max_score");
      subProjects.put("avg","$avg_score");
      projectFields.put("item_info",subProjects );
      DBObject project = new BasicDBObject("$project", projectFields);
      
      /* 将结果push到一起*/
      DBObject groupAgainFields = new BasicDBObject("_id", "$grade_name");
      groupAgainFields.put("item_info", new BasicDBObject("$push", "$item_info"));
      DBObject reshapeGroup = new BasicDBObject("$group", groupAgainFields);
      
      /* 查看Group结果 */
      AggregationOutput output1 = mongoTemplate.getCollection("pt").aggregate(unwind, group, project, reshapeGroup);
      for (Iterator<DBObject> iterator = output1.results().iterator(); iterator.hasNext();) {
          DBObject obj =iterator.next();
          System.out.println(obj.toString());
      }
      复制代码

      结果: console中:


官方文档传送门

第一章:Document定义章

第二章:简单的CRUD

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在MongoTemplate中进行排序,可以使用sort方法和Criteria对象。首先,您需要创建一个Criteria对象,用于指定排序的字段和顺序。然后,将该Criteria对象传递给sort方法,以指定排序的方式。 下面是一个示例代码,演示如何在MongoTemplate中进行排序: ```java import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; @Autowired private MongoTemplate mongoTemplate; public List<Student> getSortedStudents() { // 创建一个Criteria对象,指定排序的字段和顺序 Criteria criteria = Criteria.where("age").gte(18).lte(25); // 例如按照age字段升序排序 // 创建一个Sort对象,定义排序方式 Sort sort = Sort.by(Sort.Direction.ASC, "age"); // 创建一个Query对象,将Criteria和Sort对象传递给它 Query query = new Query().addCriteria(criteria).with(sort); // 使用MongoTemplate的find方法执行查询并返回结果 List<Student> sortedStudents = mongoTemplate.find(query, Student.class); return sortedStudents; } ``` 在上述示例中,我们首先创建了一个Criteria对象,使用where方法指定了排序的字段和范围。然后,我们创建了一个Sort对象,定义了排序的方式。接下来,我们创建了一个Query对象,并通过addCriteria方法将Criteria对象添加到Query对象中,并通过with方法将Sort对象添加到Query对象中。最后,我们使用MongoTemplate的find方法执行查询,并将结果返回。 请注意,上述示例中的Student类是一个示例实体类,您需要根据您的实际情况自定义实体类和字段。 : https://example.com : https://example.com : https://example.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值