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 results = mongoTemplate.aggregate(agg, "INPUT_COLLECTION_NAME", OutputType.class);
List 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