一组中的两个总计

Two Aggregate Totals in One Group

我在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 项。

我想像我的本机查询一样获取结果。

如果您要求 return 对“15”和 "not 15" 进行分组,那么您正在寻找 $cond 运算符,它将允许 "branching" 基于条件评估。

根据 "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,那么你可以return 一个响应的数值,这里是 1 用于 "counting" 或实际字段值,当这是你想要发送到累加器的值时。简而言之,它是一个 "ternary" 运算符或 if/then/else 条件(实际上可以用键以更具表现力的形式显示),您可以使用它来测试条件并决定 return.

对于 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() 作为 "constant" 值,例如 1 作为 "counts",以及 .thenValueOf()您实际上需要文档中某个字段的 "value",因此基本上等于 "$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")
);