Aggregation Framework Support

Spring Data MongoDB 通过 Aggregation 类提供对聚合框架的支持,该类可用于创建和执行聚合管道。它还提供了一系列聚合操作符,这些操作符映射到 MongoDB 聚合框架中的操作。

Spring Data MongoDB 还支持分面聚合,它允许您创建多维度的聚合,其中数据可以跨多个维度进行分类和分析。

此外,Spring Data MongoDB 提供对投影表达式中 SpEL 表达式的支持,允许您使用简单的算术运算和复杂的计算来定义投影字段。

Spring Data MongoDB 为 MongoDB 2.2 版中引入的聚合框架提供支持。

Spring Data MongoDB provides support for the Aggregation Framework introduced to MongoDB in version 2.2.

有关更多信息,请参阅聚合框架的完整 reference documentation 和 MongoDB 的其他数据聚合工具。

For further information, see the full reference documentation of the aggregation framework and other data aggregation tools for MongoDB.

Basic Concepts

Spring Data MongoDB 中的聚合框架支持基于以下主要抽象:“聚合”、“聚合定义”和“聚合结果”。

The Aggregation Framework support in Spring Data MongoDB is based on the following key abstractions: Aggregation, AggregationDefinition, and AggregationResults.

  • Aggregation[.iokays-translated-c37dddc2e4e7ab48dbdb4d3bbe191285] “聚合”表示 MongoDB“聚合”操作,并保存聚合管道指令的描述。聚合是通过调用 Aggregation 类的相应 newAggregation(…) 静态工厂方法创建的,该方法采用 AggregateOperation 列表和可选输入类。

An Aggregation represents a MongoDB aggregate operation and holds the description of the aggregation pipeline instructions. Aggregations are created by invoking the appropriate newAggregation(…) static factory method of the Aggregation class, which takes a list of AggregateOperation and an optional input class.

实际的聚合操作由 MongoTemplateaggregate 方法运行,该方法将所需输出类作为参数。

The actual aggregate operation is run by the aggregate method of the MongoTemplate, which takes the desired output class as a parameter. * TypedAggregation[.iokays-translated-d11e79957833771c02dad15d9cec7f2a] “类型化聚合”与“聚合”一样,保存聚合管道的指令,并引用输入类型,该输入类型用于将域属性映射到实际文档字段。

A TypedAggregation, just like an Aggregation, holds the instructions of the aggregation pipeline and a reference to the input type, that is used for mapping domain properties to actual document fields.

在运行时,针对所给输入类型检查字段引用,同时考虑潜在的 @Field 注释。

At runtime, field references get checked against the given input type, considering potential @Field annotations.

更改于 3.2 中,现在引用非存在属性不再产生错误。若要恢复先前行为,请使用 AggregationOptionsstrictMapping 选项。

Changed in 3.2 referencing non-existent properties does no longer raise errors. To restore the previous behaviour use the strictMapping option of AggregationOptions.

  • AggregationDefinition[.iokays-translated-33987af191e49e78279d08e432d94025] “聚合定义”表示 MongoDB 聚合管道操作,并描述在此聚合步骤中应执行的处理。虽然您可以手动创建 AggregationDefinition,但我们建议使用 Aggregate 类提供的静态工厂方法来构建 AggregateOperation

An AggregationDefinition represents a MongoDB aggregation pipeline operation and describes the processing that should be performed in this aggregation step. Although you could manually create an AggregationDefinition, we recommend using the static factory methods provided by the Aggregate class to construct an AggregateOperation. * AggregationResults[.iokays-translated-abd882654a71b2ffdafc5e81eaf1348e] 聚合结果 是聚合操作结果的容器。它提供对以 Document 形式的原始聚合结果、已映射对象和其他聚合相关信息的访问。

AggregationResults is the container for the result of an aggregate operation. It provides access to the raw aggregation result, in the form of a Document to the mapped objects and other information about the aggregation.

以下清单显示了使用 Spring Data MongoDB 来支持 MongoDB 聚合框架的规范示例:

The following listing shows the canonical example for using the Spring Data MongoDB support for the MongoDB Aggregation Framework:

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 方法的第一个参数提供,则 MongoTemplate 会从此类派生输入集合的名称。否则,如果您没有指定输入类,必须显式提供输入集合的名称。如果同时提供了输入类和输入集合,则后者优先。

Note that, if you provide an input class as the first parameter to the newAggregation method, the MongoTemplate derives the name of the input collection from this class. Otherwise, if you do not not specify an input class, you must provide the name of the input collection explicitly. If both an input class and an input collection are provided, the latter takes precedence.

Example 1. Supported Aggregation Operations & Stages

MongoDB 聚合框架提供以下聚合阶段和操作类型:

The MongoDB Aggregation Framework provides the following types of aggregation stages and operations:

  • addFields - AddFieldsOperation

  • bucket / bucketAuto - BucketOperation / BucketAutoOperation

  • count - CountOperation

  • densify - DensifyOperation

  • facet - FacetOperation

  • geoNear - GeoNearOperation

  • graphLookup - GraphLookupOperation

  • group - GroupOperation

  • limit - LimitOperation

  • lookup - LookupOperation

  • match - MatchOperation

  • merge - MergeOperation

  • project - ProjectionOperation

  • redact - RedactOperation

  • replaceRoot - ReplaceRootOperation

  • sample - SampleOperation

  • set - SetOperation

  • setWindowFields - SetWindowFieldsOperation

  • skip - SkipOperation

  • sort / sortByCount - SortOperation / SortByCountOperation

  • unionWith - UnionWithOperation

  • unset - UnsetOperation

  • unwind - UnwindOperation

可以通过实现 AggregationOperationAggregation.stage 提供不受支持的聚合阶段(如 MongoDB Atlas 中的 $search)。Aggregation.stage 是通过提供其 JSON 或 Bson 表示来注册管道阶段的快捷方式。

Unsupported aggregation stages (like $search for MongoDB Atlas) can be provided by implementing either AggregationOperation. Aggregation.stage is a shortcut for registering a pipeline stage by providing its JSON or Bson representation.

Aggregation.stage("""
    { $search : {
        "near": {
          "path": "released",
          "origin": { "$date": { "$numberLong": "..." } } ,
          "pivot": 7
        }
      }
    }
""");

在撰写本文时,我们为 Spring Data MongoDB 中的以下聚合操作员提供了支持:

At the time of this writing, we provide support for the following Aggregation Operators in Spring Data MongoDB:

Table 1. Aggregation Operators currently supported by Spring Data MongoDB

Set Aggregation Operators

setEquals, setIntersection, setUnion, setDifference, setIsSubset, anyElementTrue, allElementsTrue

Group/Accumulator Aggregation Operators

addToSet, bottom, bottomN, covariancePop, covarianceSamp, expMovingAvg, first, firstN, last, lastN max, maxN, min, minN, avg, push, sum, top, topN, count (*), median, percentile, stdDevPop, stdDevSamp

Arithmetic Aggregation Operators

abs, acos, acosh, add (* via plus), asin, asin, atan, atan2, atanh, ceil, cos, cosh, derivative, divide, exp, floor, integral, ln, log, log10, mod, multiply, pow, round, sqrt, subtract (* via minus), sin, sinh, tan, tanh, trunc

String Aggregation Operators

concat, substr, toLower, toUpper, strcasecmp, indexOfBytes, indexOfCP, regexFind, regexFindAll, regexMatch, replaceAll, replaceOne, split`, strLenBytes, strLenCP, substrCP, trim, ltrim, rtim

Comparison Aggregation Operators

eq (* via is), gt, gte, lt, lte, ne

Array Aggregation Operators

arrayElementAt, arrayToObject, concatArrays, filter, first, in, indexOfArray, isArray, last, range`, reverseArray, reduce, size, sortArray, slice, zip

Literal Operators

literal

Date Aggregation Operators

dateSubstract, dateTrunc, dayOfYear, dayOfMonth, dayOfWeek, year, month, week, hour, minute, second, millisecond, dateAdd, dateDiff, dateToString, dateFromString, dateFromParts, dateToParts, isoDayOfWeek, isoWeek, isoWeekYear, tsIncrement, tsSecond

Variable Operators

map

Conditional Aggregation Operators

cond, ifNull, switch

Type Aggregation Operators

type

Convert Aggregation Operators

convert, degreesToRadians, toBool, toDate, toDecimal, toDouble, toInt, toLong, toObjectId, toString

Object Aggregation Operators

objectToArray, mergeObjects, getField, setField

Script Aggregation Operators

function, accumulator

  • 此操作由 Spring Data MongoDB 映射或添加。

  • The operation is mapped or added by Spring Data MongoDB.

请注意,此处未列出的聚合操作目前不受 Spring Data MongoDB 支持。比较聚合操作符表示为 Criteria 表达式。

Note that the aggregation operations not listed here are currently not supported by Spring Data MongoDB. Comparison aggregation operators are expressed as Criteria expressions.

Projection Expressions

投影表达式用于定义特定聚合步骤的结果字段。投影表达式可以通过 Aggregation 类的 project 方法来定义,方法是传递一系列 String 对象或聚合框架 Fields 对象。可以通过使用 and(String) 方法和使用 as(String) 方法设置别名来通过流利 API 扩展投影的其他字段。请注意,您还可以使用聚合框架的 Fields.field 静态工厂方法定义带有别名的字段,然后您可以使用它构造一个新的 Fields 实例。对于后续聚合阶段中投影字段的引用仅对已包括字段或其别名的字段名称有效(包括新定义的字段及其别名)。未包含在投影中的字段无法在后续的聚合阶段中引用。以下清单显示了投影表达式的示例:

Projection expressions are used to define the fields that are the outcome of a particular aggregation step. Projection expressions can be defined through the project method of the Aggregation class, either by passing a list of String objects or an aggregation framework Fields object. The projection can be extended with additional fields through a fluent API by using the and(String) method and aliased by using the as(String) method. Note that you can also define fields with aliases by using the Fields.field static factory method of the aggregation framework, which you can then use to construct a new Fields instance. References to projected fields in later aggregation stages are valid only for the field names of included fields or their aliases (including newly defined fields and their aliases). Fields not included in the projection cannot be referenced in later aggregation stages. The following listings show examples of projection expression:

Example 2. Projection expression examples
// 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")
Example 3. Multi-Stage Aggregation using Projection and Sorting
// 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")

可以在 AggregationTests 类中找到有关项目操作的更多示例。请注意,可以在 MongoDB 聚合框架参考文档 corresponding section 中找到有关投影表达式的更多详细信息。

More examples for project operations can be found in the AggregationTests class. Note that further details regarding the projection expressions can be found in the corresponding section of the MongoDB Aggregation Framework reference documentation.

Faceted Classification

3.4 版及更高版本,MongoDB 使用聚合框架支持多方面分类。多方面分类使用语义类别(通用或特定于主题)组合来创建完整的分类项。流经聚合管道的文档被分类为各种类别。多方面分类能够在同一组输入文档上启用各种聚合,而无需多次检索输入文档。

As of Version 3.4, MongoDB supports faceted classification by using the Aggregation Framework. A faceted classification uses semantic categories (either general or subject-specific) that are combined to create the full classification entry. Documents flowing through the aggregation pipeline are classified into buckets. A multi-faceted classification enables various aggregations on the same set of input documents, without needing to retrieve the input documents multiple times.

Buckets

基于指定的表达式和桶边界,桶操作将传入的文档分类为组(称为桶)。桶操作需要一个分组字段或分组表达式。可以使用 Aggregate 类的 bucket()bucketAuto() 方法来定义它们。BucketOperationBucketAutoOperation 可以基于输入文档的聚合表达式公开累积。可以通过使用 with…() 方法和 andOutput(String) 方法通过流利 API 使用其他参数来扩展桶操作。可以使用 as(String) 方法为该操作设置别名。每个桶在输出中表示为一个文档。

Bucket operations categorize incoming documents into groups, called buckets, based on a specified expression and bucket boundaries. Bucket operations require a grouping field or a grouping expression. You can define them by using the bucket() and bucketAuto() methods of the Aggregate class. BucketOperation and BucketAutoOperation can expose accumulations based on aggregation expressions for input documents. You can extend the bucket operation with additional parameters through a fluent API by using the with…() methods and the andOutput(String) method. You can alias the operation by using the as(String) method. Each bucket is represented as a document in the output.

BucketOperation 获取一组已定义的边界,将传入的文档分组到这些类别中。需要对边界进行排序。以下清单显示了一些桶操作的示例:

BucketOperation takes a defined set of boundaries to group incoming documents into these categories. Boundaries are required to be sorted. The following listing shows some examples of bucket operations:

Example 4. Bucket operation examples
// 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 会确定边界,以尝试将文档平均分布到指定数量的存储段中。BucketAutoOperation 可选采用一个粒度值,指定 preferred number 序列的使用,以确保计算的边界边缘以较优的圆整数字或 10 的幂结束。以下列表显示了存储段操作示例:

BucketAutoOperation determines boundaries in an attempt to evenly distribute documents into a specified number of buckets. BucketAutoOperation optionally takes a granularity value that specifies the preferred number series to use to ensure that the calculated boundary edges end on preferred round numbers or on powers of 10. The following listing shows examples of bucket operations:

Example 5. Bucket operation examples
// 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");

若要在存储段中创建输出字段,存储段操作可以使用 AggregationExpressionandOutput()SpEL expressionsandOutputExpression()

To create output fields in buckets, bucket operations can use AggregationExpression through andOutput() and SpEL expressions through andOutputExpression().

请注意,可以在 MongoDB 聚合框架参考文档的 $bucket section$bucketAuto section 中找到有关存储段表达式的更多详细信息。

Note that further details regarding bucket expressions can be found in the $bucket section and $bucketAuto section of the MongoDB Aggregation Framework reference documentation.

Multi-faceted Aggregation

可以使用多个聚合管道来创建多方面的聚合,它们可以在单个聚合阶段中表征多个维度(或方面)中的数据。多方面的聚合提供了多个过滤器和分类以指导数据浏览和分析。分面的一个常见实现是许多在线零售商如何通过对产品价格、制造商、尺寸和其他因素应用过滤器来缩小搜索结果范围。

Multiple aggregation pipelines can be used to create multi-faceted aggregations that characterize data across multiple dimensions (or facets) within a single aggregation stage. Multi-faceted aggregations provide multiple filters and categorizations to guide data browsing and analysis. A common implementation of faceting is how many online retailers provide ways to narrow down search results by applying filters on product price, manufacturer, size, and other factors.

可以使用 Aggregation 类的 facet() 方法定义 FacetOperation。可以使用 and() 方法使用多个聚合管道来对其进行自定义。每个子管道在其输出文档中都有自己的字段,其中它的结果作为文档数组存储。

You can define a FacetOperation by using the facet() method of the Aggregation class. You can customize it with multiple aggregation pipelines by using the and() method. Each sub-pipeline has its own field in the output document where its results are stored as an array of documents.

在分组之前,子管道可以投影并过滤输入文档。常见的用例包括提取日期部件或在分类之前进行计算。以下清单显示了分面操作示例:

Sub-pipelines can project and filter input documents prior to grouping. Common use cases include extraction of date parts or calculations before categorization. The following listing shows facet operation examples:

Example 6. Facet operation examples
// 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"))

请注意,可以在 MongoDB 聚合框架参考文档的 $facet section 中找到有关方面操作的更多详细信息。

Note that further details regarding facet operation can be found in the $facet section of the MongoDB Aggregation Framework reference documentation.

Sort By Count

按计数排序操作根据指定表达式的值对传入的文档进行分组,计算每个不同组中文档的计数,并按计数对结果进行排序。它提供了一个在使用 Faceted Classification 时应用排序的便捷快捷方式。按计数排序操作需要一个分组字段或分组表达式。以下列表显示按计数排序示例:

Sort by count operations group incoming documents based on the value of a specified expression, compute the count of documents in each distinct group, and sort the results by count. It offers a handy shortcut to apply sorting when using Faceted Classification. Sort by count operations require a grouping field or grouping expression. The following listing shows a sort by count example:

Example 7. Sort by count example
// generates { $sortByCount: "$country" }
sortByCount("country");

按计数排序操作等效于以下 BSON(二进制 JSON):

A sort by count operation is equivalent to the following BSON (Binary JSON):

{ $group: { _id: <expression>, count: { $sum: 1 } } },
{ $sort: { count: -1 } }

Spring Expression Support in Projection Expressions

我们通过 ProjectionOperationBucketOperation 类的 andExpression 方法支持在投影表达式中使用 SpEL 表达式。此功能允许您将所需的表达式定义为 SpEL 表达式。在运行查询时,SpEL 表达式将转换为相应的 MongoDB 投影表达式部分。这种安排使得表达复杂的计算变得更加容易。

We support the use of SpEL expressions in projection expressions through the andExpression method of the ProjectionOperation and BucketOperation classes. This feature lets you define the desired expression as a SpEL expression. On running a query, the SpEL expression is translated into a corresponding MongoDB projection expression part. This arrangement makes it much easier to express complex calculations.

Complex Calculations with SpEL expressions

考虑以下 SpEL 表达式:

Consider the following SpEL expression:

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]}
    ]
}]}

您可以在 Aggregation Framework Example 5Aggregation Framework Example 6 中看到更多上下文示例。您可以在 SpelExpressionTransformerUnitTests 中找到更多受支持的 SpEL 表达式构造的用法示例。

You can see examples in more context in Aggregation Framework Example 5 and Aggregation Framework Example 6. You can find more usage examples for supported SpEL expression constructs in SpelExpressionTransformerUnitTests.

Example 8. Supported SpEL transformations
SpEL Expression Mongo Expression Part

a == b

{ $eq : [$a, $b] }

a != b

{ $ne : [$a , $b] }

a > b

{ $gt : [$a, $b] }

a >= b

{ $gte : [$a, $b] }

a < b

{ $lt : [$a, $b] }

a ⇐ b

{ $lte : [$a, $b] }

a + b

{ $add : [$a, $b] }

a - b

{ $subtract : [$a, $b] }

a * b

{ $multiply : [$a, $b] }

a / b

{ $divide : [$a, $b] }

a^b

{ $pow : [$a, $b] }

a % b

{ $mod : [$a, $b] }

a && b

{ $and : [$a, $b] }

a

b

{ $or : [$a, $b] }

!a

{ $not : [$a] }

除了上表所示的转换之外,您还可以使用标准 SpEL 操作(例如 new),例如创建数组,并通过名称(后跟括号中使用的参数)来引用表达式。以下示例展示了如何用这种方式创建数组:

In addition to the transformations shown in the preceding table, you can use standard SpEL operations such as new to (for example) create arrays and reference expressions through their names (followed by the arguments to use in brackets). The following example shows how to create an array in this fashion:

// { $setEquals : [$a, [5, 8, 13] ] }
.andExpression("setEquals(a, new int[]{5, 8, 13})");

Aggregation Framework Examples

本节中的示例演示了 MongoDB 聚合框架与 Spring Data MongoDB 的用法模式。

The examples in this section demonstrate the usage patterns for the MongoDB Aggregation Framework with Spring Data MongoDB.

Aggregation Framework Example 1

在这个入门示例中,我们要聚合标签列表,从按发生次数降序排序的 MongoDB 集合(称为 tags)中获取特定标签的发生次数。该示例演示了分组、排序、投影(选择)和展开(结果拆分)的用法。

In this introductory example, we want to aggregate a list of tags to get the occurrence count of a particular tag from a MongoDB collection (called tags) sorted by the occurrence count in descending order. This example demonstrates the usage of grouping, sorting, projections (selection), and unwinding (result splitting).

class TagCount {
 String tag;
 int n;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

Aggregation agg = newAggregation(
    project("tags"),
    unwind("tags"),
    group("tags").count().as("n"),
    project("n").and("tag").previousOperation(),
    sort(DESC, "n")
);

AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, "tags", TagCount.class);
List<TagCount> tagCount = results.getMappedResults();

先前列表使用以下算法:

The preceding listing uses the following algorithm:

  1. Create a new aggregation by using the newAggregation static factory method, to which we pass a list of aggregation operations. These aggregate operations define the aggregation pipeline of our Aggregation.

  2. Use the project operation to select the tags field (which is an array of strings) from the input collection.

  3. Use the unwind operation to generate a new document for each tag within the tags array.

  4. Use the group operation to define a group for each tags value for which we aggregate the occurrence count (by using the count aggregation operator and collecting the result in a new field called n).

  5. Select the n field and create an alias for the ID field generated from the previous group operation (hence the call to previousOperation()) with a name of tag.

  6. Use the sort operation to sort the resulting list of tags by their occurrence count in descending order.

  7. Call the aggregate method on MongoTemplate to let MongoDB perform the actual aggregation operation, with the created Aggregation as an argument.

请注意,输入集合已被明确指定为 aggregate 方法的 tags 参数。如果输入集合的名称未明确指定,则它将派生自作为 newAggreation 方法的第一个参数传递的输入类。

Note that the input collection is explicitly specified as the tags parameter to the aggregate Method. If the name of the input collection is not specified explicitly, it is derived from the input class passed as the first parameter to the newAggreation method.

Aggregation Framework Example 2

此示例基于 MongoDB 聚合框架文档中的 Largest and Smallest Cities by State 示例。我们添加了额外的排序以生成在不同 MongoDB 版本中具有稳定性的结果。在这里,我们希望通过使用聚合框架为每个州返回人口最少和人口最多的城市。此示例演示了分组、排序和投影(选择)。

This example is based on the Largest and Smallest Cities by State example from the MongoDB Aggregation Framework documentation. We added additional sorting to produce stable results with different MongoDB versions. Here we want to return the smallest and largest cities by population for each state by using the aggregation framework. This example demonstrates grouping, sorting, and projections (selection).

class ZipInfo {
   String id;
   String city;
   String state;
   @Field("pop") int population;
   @Field("loc") double[] location;
}

class City {
   String name;
   int population;
}

class ZipInfoStats {
   String id;
   String state;
   City biggestCity;
   City smallestCity;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class,
    group("state", "city")
       .sum("population").as("pop"),
    sort(ASC, "pop", "state", "city"),
    group("state")
       .last("city").as("biggestCity")
       .last("pop").as("biggestPop")
       .first("city").as("smallestCity")
       .first("pop").as("smallestPop"),
    project()
       .and("state").previousOperation()
       .and("biggestCity")
          .nested(bind("name", "biggestCity").and("population", "biggestPop"))
       .and("smallestCity")
          .nested(bind("name", "smallestCity").and("population", "smallestPop")),
    sort(ASC, "state")
);

AggregationResults<ZipInfoStats> result = mongoTemplate.aggregate(aggregation, ZipInfoStats.class);
ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0);

请注意,ZipInfo 类映射给定输入集合的结构。ZipInfoStats 类以所需的输出格式定义结构。

Note that the ZipInfo class maps the structure of the given input-collection. The ZipInfoStats class defines the structure in the desired output format.

前述列表使用以下算法:

The preceding listings use the following algorithm:

  1. Use the group operation to define a group from the input-collection. The grouping criteria is the combination of the state and city fields, which forms the ID structure of the group. We aggregate the value of the population property from the grouped elements by using the sum operator and save the result in the pop field.

  2. Use the sort operation to sort the intermediate-result by the pop, state and city fields, in ascending order, such that the smallest city is at the top and the biggest city is at the bottom of the result. Note that the sorting on state and city is implicitly performed against the group ID fields (which Spring Data MongoDB handled).

  3. Use a group operation again to group the intermediate result by state. Note that state again implicitly references a group ID field. We select the name and the population count of the biggest and smallest city with calls to the last(…) and first(…​) operators, respectively, in the project operation.

  4. Select the state field from the previous group operation. Note that state again implicitly references a group ID field. Because we do not want an implicitly generated ID to appear, we exclude the ID from the previous operation by using and(previousOperation()).exclude(). Because we want to populate the nested City structures in our output class, we have to emit appropriate sub-documents by using the nested method.

  5. Sort the resulting list of StateStats by their state name in ascending order in the sort operation.

请注意,我们从作为 newAggregation 方法的第一个参数传递的 ZipInfo 类派生输入集合的名称。

Note that we derive the name of the input collection from the ZipInfo class passed as the first parameter to the newAggregation method.

Aggregation Framework Example 3

此示例基于 MongoDB 聚合框架文档中的 States with Populations Over 10 Million 示例。我们添加了额外的排序以生成在不同 MongoDB 版本中具有稳定性的结果。在这里,我们希望通过使用聚合框架,返回人口超过 1 千万的所有州。此示例演示了分组、排序和匹配(筛选)。

This example is based on the States with Populations Over 10 Million example from the MongoDB Aggregation Framework documentation. We added additional sorting to produce stable results with different MongoDB versions. Here we want to return all states with a population greater than 10 million, using the aggregation framework. This example demonstrates grouping, sorting, and matching (filtering).

class StateStats {
   @Id String id;
   String state;
   @Field("totalPop") int totalPopulation;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<ZipInfo> agg = newAggregation(ZipInfo.class,
    group("state").sum("population").as("totalPop"),
    sort(ASC, previousOperation(), "totalPop"),
    match(where("totalPop").gte(10 * 1000 * 1000))
);

AggregationResults<StateStats> result = mongoTemplate.aggregate(agg, StateStats.class);
List<StateStats> stateStatsList = result.getMappedResults();

前述列表使用以下算法:

The preceding listings use the following algorithm:

  1. Group the input collection by the state field and calculate the sum of the population field and store the result in the new field "totalPop".

  2. Sort the intermediate result by the id-reference of the previous group operation in addition to the "totalPop" field in ascending order.

  3. Filter the intermediate result by using a match operation which accepts a Criteria query as an argument.

请注意,我们从作为 newAggregation 方法的第一个参数传递的 ZipInfo 类派生输入集合的名称。

Note that we derive the name of the input collection from the ZipInfo class passed as first parameter to the newAggregation method.

Aggregation Framework Example 4

此示例演示在投影操作中使用简单的算术运算。

This example demonstrates the use of simple arithmetic operations in the projection operation.

class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .and("netPrice").plus(1).as("netPricePlus1")
        .and("netPrice").minus(1).as("netPriceMinus1")
        .and("netPrice").multiply(1.19).as("grossPrice")
        .and("netPrice").divide(2).as("netPriceDiv2")
        .and("spaceUnits").mod(2).as("spaceUnitsMod2")
);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();

请注意,我们从作为 newAggregation 方法的第一个参数传递的 Product 类派生输入集合的名称。

Note that we derive the name of the input collection from the Product class passed as first parameter to the newAggregation method.

Aggregation Framework Example 5

此示例演示在投影操作中使用从 SpEL 表达式派生的简单算术运算。

This example demonstrates the use of simple arithmetic operations derived from SpEL Expressions in the projection operation.

class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .andExpression("netPrice + 1").as("netPricePlus1")
        .andExpression("netPrice - 1").as("netPriceMinus1")
        .andExpression("netPrice / 2").as("netPriceDiv2")
        .andExpression("netPrice * 1.19").as("grossPrice")
        .andExpression("spaceUnits % 2").as("spaceUnitsMod2")
        .andExpression("(netPrice * 0.8  + 1.2) * 1.19").as("grossPriceIncludingDiscountAndCharge")

);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();

Aggregation Framework Example 6

此示例演示在投影操作中使用从 SpEL 表达式派生的复杂算术运算。

This example demonstrates the use of complex arithmetic operations derived from SpEL Expressions in the projection operation.

注意:传递给 addExpression 方法的附加参数可以根据其位置通过索引器表达式来引用。在此示例中,我们通过 [0] 引用参数数组的第一个参数。当 SpEL 表达式被转换到 MongoDB 聚合框架表达式时,外部参数表达式将被替换为它们各自的值。

Note: The additional parameters passed to the addExpression method can be referenced with indexer expressions according to their position. In this example, we reference the first parameter of the parameters array with [0]. When the SpEL expression is transformed into a MongoDB aggregation framework expression, external parameter expressions are replaced with their respective values.

class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

double shippingCosts = 1.2;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .andExpression("(netPrice * (1-discountRate)  + [0]) * (1+taxRate)", shippingCosts).as("salesPrice")
);

AggregationResults<Document> result = mongoTemplate.aggregate(agg, Document.class);
List<Document> resultList = result.getMappedResults();

请注意,我们还可以在 SpEL 表达式中引用文档的其他字段。

Note that we can also refer to other fields of the document within the SpEL expression.

Aggregation Framework Example 7

此示例使用条件投影。它派生自 $cond reference documentation

This example uses conditional projection. It is derived from the $cond reference documentation.

public class InventoryItem {

  @Id int id;
  String item;
  String description;
  int qty;
}

public class InventoryItemProjection {

  @Id int id;
  String item;
  String description;
  int qty;
  int discount
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<InventoryItem> agg = newAggregation(InventoryItem.class,
  project("item").and("discount")
    .applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250))
      .then(30)
      .otherwise(20))
    .and(ifNull("description", "Unspecified")).as("description")
);

AggregationResults<InventoryItemProjection> result = mongoTemplate.aggregate(agg, "inventory", InventoryItemProjection.class);
List<InventoryItemProjection> stateStatsList = result.getMappedResults();

此一步聚合将使用投影操作与 inventory 集合。我们使用条件操作针对所有 qty 大于或等于 250 的库存项投影 discount 字段。针对 description 字段执行第二次条件投影。我们将 Unspecified 描述应用到所有既没有 description 字段又有 null 描述的项。

This one-step aggregation uses a projection operation with the inventory collection. We project the discount field by using a conditional operation for all inventory items that have a qty greater than or equal to 250. A second conditional projection is performed for the description field. We apply the Unspecified description to all items that either do not have a description field or items that have a null description.

从 MongoDB 3.6 开始,可以通过使用条件表达式从投影中排除字段。

As of MongoDB 3.6, it is possible to exclude fields from the projection by using a conditional expression.

Example 9. Conditional aggregation projection
TypedAggregation<Book> agg = Aggregation.newAggregation(Book.class,
  project("title")
    .and(ConditionalOperators.when(ComparisonOperators.valueOf("author.middle")     1
        .equalToValue(""))                                                          2
        .then("$$REMOVE")                                                           3
        .otherwiseValueOf("author.middle")                                          4
    )
	.as("author.middle"));
1 If the value of the field author.middle
2 does not contain a value,
3 then use $$REMOVE to exclude the field.
4 Otherwise, add the field value of author.middle.