一组中的两个总计

时间:2018-05-01 20:31:00

标签: java mongodb aggregation-framework spring-data-mongodb

我在 MongoDB 中写了一个查询,如下所示:

db.getCollection('student').aggregate(
[
  {
      $match: { "student_age" : { "$ne" : 15 } }
  },
  {
      $group: 
      {
        _id: "$student_name", 
        count: {$sum: 1},
        sum1: {$sum: "$student_age"}
      }
  }     
])

换句话说,我想取出15岁以下的学生人数和他们年龄的总结。查询工作正常,我得到两个数据项。

在我的应用程序中,我想通过 Spring Data 进行查询。 我写了以下代码:

Criteria where = Criteria.where("AGE").ne(15);
Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(where),
                Aggregation.group().sum("student_age").as("totalAge"),
                count().as("countOfStudentNot15YearsOld"));

运行此代码时,输​​出查询将为:

"aggregate" : "MyDocument", "pipeline" : 
    [           { "$match"   { "AGE" : { "$ne" : 15 } } }, 
                { "$group" : { "_id" : null, "totalAge" : { "$sum" : "$student_age" } } }, 
                { "$count" : "countOfStudentNot15YearsOld" }], 
                  "cursor" : { "batchSize" : 2147483647 } 

不幸的是,结果只是countOfStudentNot15YearsOld项。

我想像我的原生查询一样获取结果。

1 个答案:

答案 0 :(得分:1)

如果您要求返回“15”和“非15”的分组,那么您正在寻找$cond运算符,该运算符将允许基于条件评估的“分支”。

从“shell”内容中你可以像这样使用它:

db.getCollection('student').aggregate([
  { "$group": {
    "_id": null,
    "countFiteen": { 
      "$sum": {
        "$cond": [{ "$eq": [ "$student_age", 15 ] }, 1, 0 ]
      }
    },
    "countNotFifteen": {
      "$sum": {
        "$cond": [{ "$ne": [ "$student_age", 15 ] }, 1, 0 ]
      }
    },
     "sumNotFifteen": {
      "$sum": {
        "$cond": [{ "$ne": [ "$student_age", 15 ] }, "$student_age", 0 ]
      }
    }
  }}
])

因此,您使用$cond执行逻辑测试,在这种情况下,当前文档中的"student_age"是否为15,那么您可以返回一个数值作为响应,这里是1用于“计数”或实际字段值,而这是您想要发送到累加器的内容。简而言之,它是一个“三元”操作符或if/then/else条件(实际上可以用更具表现力的形式用键显示),您可以用它来测试条件并决定返回什么。

对于spring mongodb实现,使用ConditionalOperators.Cond构造相同的BSON表达式:

import org.springframework.data.mongodb.core.aggregation.*;

ConditionalOperators.Cond isFifteen = ConditionalOperators.when(new Criteria("student_age").is(15))
   .then(1).otherwise(0);

ConditionalOperators.Cond notFifteen = ConditionalOperators.when(new Criteria("student_age").ne(15))
        .then(1).otherwise(0);

ConditionalOperators.Cond sumNotFifteen = ConditionalOperators.when(new Criteria("student_age").ne(15))
        .thenValueOf("student_age").otherwise(0);


GroupOperation groupStage =  Aggregation.group()
        .sum(isFifteen).as("countFifteen")
        .sum(notFifteen).as("countNotFifteen")
        .sum(sumNotFifteen).as("sumNotFifteen");

Aggregation aggregation = Aggregation.newAggregation(groupStage);

所以基本上你只是延伸了这个逻辑,使用.then()获取“常量”值,例如1用于“计数”,.thenValueOf()用于实际需要“值“来自文档的字段,因此基本上等于"$student_age",如公共shell表示法所示。

由于ConditionalOperators.Cond共享AggregationExpression接口,因此可以与接受.sum()而不是字符串的形式的AggregationExpression一起使用。这是对spring mongo的过去版本的改进,这将要求您执行$project阶段,以便在执行$group之前为评估表达式提供实际文档属性。

如果你想要的只是复制spring mongodb的原始查询,那么你的错误是使用$count聚合阶段,而不是追加到group()

Criteria where = Criteria.where("AGE").ne(15);
Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(where),
                Aggregation.group()
                    .sum("student_age").as("totalAge")
                    .count().as("countOfStudentNot15YearsOld")
);