Spring data mongo Aggregate
Spring Data Mongo 支持MongoDB引入的聚类框架。
基本概念
-
Aggregation : 聚类 ,表示MongoDB的aggregate 操作,它保存aggregation Pipeline的命令。 通过Aggregation类类表示, 该类有一个AggregateOperation列表和其他输入类。
实际执行过程是通过MongoTemplate 来执行的。 -
AggregationOperation 聚类操作: 表示一个MongoDB aggregation pipeline 操作,描述聚类执行的步骤 。 尽管可以手工创建一个AggregationOperation , 但是建议使用Aggregate类提供的静态工厂方法来创建。
-
AggregationResults 聚类结果: 聚类操作的结果容器。 提供以document形式访问的操作。
如下示例是一个典型的聚类操作:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
Aggregation agg = newAggregation(
pipelineOP1(),
pipelineOP2(),
pipelineOPn()
);
AggregationResults<OutputType> results = mongoTemplate.aggregate(agg, "INPUT_COLLECTION_NAME", OutputType.class);
List<OutputType> mappedResult = results.getMappedResults();
注意: 若newAggregation 的第一个参数接收一个input class , 则MongoTemplate 调用aggregate 时不需要提供collection , 若两者都提供了, 则优先使用输入的collection。
支持的聚类操作
MongoDB 本身支持的聚类操作:
-
Pipeline Aggregation Operators
-
Group Aggregation Operators
-
Boolean Aggregation Operators
-
Comparison Aggregation Operators
-
Arithmetic Aggregation Operators
-
String Aggregation Operators
-
Date Aggregation Operators
-
Array Aggregation Operators
-
Conditional Aggregation Operators
-
Lookup Aggregation Operators
Spring data mongodb 支持的:

Projection 表达式
Projection 表达式用来定义在聚合步骤的输出结果 。 该表达式通过Aggregation类的project方法定义, 输入时String列表或Fields对象。 表达式可以通过API使用and进行扩展,使用as进行别名。 同样可以使用Fields.field静态工厂方法来定义fields。
后续聚合阶段可以使用这些fields。
例子:
// generates {$project: {name: 1, netPrice: 1}}
project("name", "netPrice")
// generates {$project: {thing1: $thing2}}
project().and("thing1").as("thing2")
// generates {$project: {a: 1, b: 1, thing2: $thing1}}
project("a","b").and("thing1").as("thing2")
// generates {$project: {name: 1, netPrice: 1}}, {$sort: {name: 1}}
project("name", "netPrice"), sort(ASC, "name")
// generates {$project: {name: $firstname}}, {$sort: {name: 1}}
project().and("firstname").as("name"), sort(ASC, "name")
// does not work
project().and("firstname").as("name"), sort(ASC, "firstname")
Faceted Classification
从Mongodb 3.4版本开始, 支持分面分类faceted classification 。 它使用组合语义(一般或特殊)来创建完整的分类条目。 流经聚合管道的document被分类为多个桶。 多面分类允许在同一组输入document上进行各种聚合,而无需多次检索输入文档。
Buckets 桶
桶操作根据特定的表达式和桶边界将输入document 分组,称为桶buckets 。
桶操作需要分组字段或者分组表达式 。 使用Aggregate 类的bucket() and bucketAuto() 来定义。
BucketOperation and BucketAutoOperation 能够根据聚合表达式对输入文档进行连续处理。 可以通过fluent API 使用with 和andOutput方法来扩展桶操作。 可以通过as方法来重命名操作。 每个桶都在output以document的形式表示。
例子:
// generates {$bucket: {groupBy: $price, boundaries: [0, 100, 400]}}
bucket("price").withBoundaries(0, 100, 400);
// generates {$bucket: {groupBy: $price, default: "Other" boundaries: [0, 100]}}
bucket("price").withBoundaries(0, 100).withDefault("Other");
// generates {$bucket: {groupBy: $price, boundaries: [0, 100], output: { count: { $sum: 1}}}}
bucket("price").withBoundaries(0, 100).andOutputCount().as("count");
// generates {$bucket: {groupBy: $price, boundaries: [0, 100], 5, output: { titles: { $push: "$title"}}}
bucket("price").withBoundaries(0, 100).andOutput("title").push().as("titles");
BucketAutoOperation 自动确定边界, 尝试均匀分布 。
它支持采用粒度值, 用于确保计算的边界边缘以首选轮数或10的幂结束的首选数字序列。
// generates {$bucketAuto: {groupBy: $price, buckets: 5}}
bucketAuto("price", 5)
// generates {$bucketAuto: {groupBy: $price, buckets: 5, granularity: "E24"}}
bucketAuto("price", 5).withGranularity(Granularities.E24).withDefault("Other");
// generates {$bucketAuto: {groupBy: $price, buckets: 5, output: { titles: { $push: "$title"}}}
bucketAuto("price", 5).andOutput("title").push().as("titles");
创建输出值使用AggregationExpression 通过andOutPut() 和 SpEL表达式通过andOutputExpression()。
Multi-faceted Aggregation
可以使用多个聚合管道来创建多面聚合,以在单个聚合阶段内跨多个维度(或构面)表征数据。 多面聚合提供多个过滤器和分类,以指导数据浏览和分析。
通过Aggregation 类的 facet()方法定义一个 FacetOperation 。 可以使用and方法来自定义多个聚合管道。 每个子管道都有自己的输出。
子管道可以在分组之前投影和过滤输入文档。 常见用例包括在分类之前提取日期部分或计算。
// generates {$facet: {categorizedByPrice: [ { $match: { price: {$exists : true}}}, { $bucketAuto: {groupBy: $price, buckets: 5}}]}}
facet(match(Criteria.where("price").exists(true)), bucketAuto("price", 5)).as("categorizedByPrice"))
// generates {$facet: {categorizedByCountry: [ { $match: { country: {$exists : true}}}, { $sortByCount: "$country"}]}}
facet(match(Criteria.where("country").exists(true)), sortByCount("country")).as("categorizedByCountry"))
// generates {$facet: {categorizedByYear: [
// { $project: { title: 1, publicationYear: { $year: "publicationDate"}}},
// { $bucketAuto: {groupBy: $price, buckets: 5, output: { titles: {$push:"$title"}}}
// ]}}
facet(project("title").and("publicationDate").extractYear().as("publicationYear"),
bucketAuto("publicationYear", 5).andOutput("title").push().as("titles"))
.as("categorizedByYear"))
SpEL 表达式支持projection
在ProjectionOperation and BucketOperation类的andExpression方法中使用spEL表达式。
会将spel表达式转换为MongoDB的projection expression 。
例: 如下表达式及转换后的projection 表达式 :
1 + (q + 1) / (q - 1)
The preceding expression is translated into the following projection expression part:
{ "$add" : [ 1, {
"$divide" : [ {
"$add":["$q", 1]}, {
"$subtract":[ "$q", 1]}
]
}]}
表达式对应关系如下:

除此之外, 还可以在spel表达式中使用new 。
// { $setEquals : [$a, [5, 8, 13] ] }
.andExpression("setEquals(a, new int[]{5, 8, 13})");
其他聚合的使用示例参见: https://docs.spring.io/spring-data/mongodb/docs/2.0.8.RELEASE/reference/html