Docs 菜单
Docs 主页
/ / /
Kotlin 协程
/

聚合(Aggregation)

在此页面上

  • 概述
  • 聚合和查找操作比较
  • 实用参考资料
  • 示例数据
  • 基本聚合
  • 解释聚合
  • 聚合表达式

在本指南中,您可以了解如何在 MongoDB Kotlin 驱动程序中使用聚合操作

聚合操作处理 MongoDB 集合中的数据并返回计算结果。 MongoDB 的聚合管道是查询 API 的一部分,以数据处理管道的概念为模型。文档进入多阶段管道,该管道将文档转换为聚合结果。

可以将聚合理解为汽车工厂。 汽车工厂内有一条装配线,沿线有配备专门工具来完成特定作业的装配站,例如钻头和焊机。毛坯零件进入工厂,然后被转换并组装成成品。

聚合管道是装配线,聚合阶段是装配站,操作符表达式则是专用工具。

使用 find操作,您可以:

  • 选择要返回哪些文档

  • 选择要返回哪些字段

  • 对结果进行排序

使用aggregation操作,您可以:

  • 执行所有find操作

  • 重命名字段

  • 计算字段

  • 汇总数据

  • 对值进行分组

您必须记住聚合操作具有一些限制

  • 返回的文档不得违反 BSON 文档大小限制(16 兆字节)。

  • 默认情况下,管道阶段的内存限制为 100 MB。如果需要,可使用 allowDiskUse 方法超过此限制。

    重要

    $graphLookup 异常

    $graphLookup 阶段存在 100 兆字节的严格内存限制,且会忽略 allowDiskUse

  • 聚合管道

  • 聚合阶段

  • 操作符表达式

  • 聚合构建器

这些示例使用 MongoDB 中的以下数据的 collection:

[
{"name": "Sun Bakery Trattoria", "contact": {"phone": "386-555-0189", "email": "SunBakeryTrattoria@example.org", "location": [-74.0056649, 40.7452371]}, "stars": 4, "categories": ["Pizza", "Pasta", "Italian", "Coffee", "Sandwiches"]},
{"name": "Blue Bagels Grill", "contact": {"phone": "786-555-0102", "email": "BlueBagelsGrill@example.com", "location": [-73.92506, 40.8275556]}, "stars": 3, "categories": ["Bagels", "Cookies", "Sandwiches"]},
{"name": "XYZ Bagels Restaurant", "contact": {"phone": "435-555-0190", "email": "XYZBagelsRestaurant@example.net", "location": [-74.0707363, 40.59321569999999]}, "stars": 4, "categories": ["Bagels", "Sandwiches", "Coffee"]},
{"name": "Hot Bakery Cafe", "contact": {"phone": "264-555-0171", "email": "HotBakeryCafe@example.net", "location": [-73.96485799999999, 40.761899]}, "stars": 4, "categories": ["Bakery", "Cafe", "Coffee", "Dessert"]},
{"name": "Green Feast Pizzeria", "contact": {"phone": "840-555-0102", "email": "GreenFeastPizzeria@example.com", "location": [-74.1220973, 40.6129407]}, "stars": 2, "categories": ["Pizza", "Italian"]},
{"name": "ZZZ Pasta Buffet", "contact": {"phone": "769-555-0152", "email": "ZZZPastaBuffet@example.com", "location": [-73.9446421, 40.7253944]}, "stars": 0, "categories": ["Pasta", "Italian", "Buffet", "Cafeteria"]},
{"name": "XYZ Coffee Bar", "contact": {"phone": "644-555-0193", "email": "XYZCoffeeBar@example.net", "location": [-74.0166091, 40.6284767]}, "stars": 5, "categories": ["Coffee", "Cafe", "Bakery", "Chocolates"]},
{"name": "456 Steak Restaurant", "contact": {"phone": "990-555-0165", "email": "456SteakRestaurant@example.com", "location": [-73.9365108, 40.8497077]}, "stars": 0, "categories": ["Steak", "Seafood"]},
{"name": "456 Cookies Shop", "contact": {"phone": "604-555-0149", "email": "456CookiesShop@example.org", "location": [-73.8850023, 40.7494272]}, "stars": 4, "categories": ["Bakery", "Cookies", "Cake", "Coffee"]},
{"name": "XYZ Steak Buffet", "contact": {"phone": "229-555-0197", "email": "XYZSteakBuffet@example.org", "location": [-73.9799932, 40.7660886]}, "stars": 3, "categories": ["Steak", "Salad", "Chinese"]}
]

collection中的数据由以下Restaurant数据类进行建模:

data class Restaurant(
val name: String,
val contact: Contact,
val stars: Int,
val categories: List<String>
) {
data class Contact(
val phone: String,
val email: String,
val location: List<Double>
)
}

如需执行聚合,请向 MongoCollection.aggregate() 方法传递聚合阶段列表。

Kotlin 驱动程序提供了 辅助类,其中包含聚合阶段的构建器。

在以下示例中,聚合管道:

  • 使用 $match 阶段来过滤其 categories 数组字段包含 Bakery 元素的文档。该示例使用 Aggregates.match 来构建 $match 阶段。

  • 使用 $group 阶段根据 stars 字段对匹配的文档进行分组,从而为每个不同的 stars 值累积文档数。

提示

另请参阅:

您可以使用聚合构建器构建此示例中使用的表达式。

data class Results(@BsonId val id: Int, val count: Int)
val resultsFlow = collection.aggregate<Results>(
listOf(
Aggregates.match(Filters.eq(Restaurant::categories.name, "Bakery")),
Aggregates.group("\$${Restaurant::stars.name}",
Accumulators.sum("count", 1))
)
)
resultsFlow.collect { println(it) }
Results(id=4, count=2)
Results(id=5, count=1)

有关本节中提到的方法和类的详情,请参阅以下 API 文档:

要查看有关 MongoDB 如何执行您的操作的信息,请使用 AggregateFlow 类的 explain() 方法。explain() 方法返回执行计划和性能统计信息。执行计划是 MongoDB 完成操作的一种潜在方式。explain() 方法同时提供获胜计划(即 MongoDB 执行的计划)和被拒绝的计划。

您可以通过将详细程度传递给 explain() 方法来指定说明的详细程度。

下表显示了说明的所有详细级别及其预计使用案例:

详细程度
用例(Use Case)
ALL_PLANS_EXECUTIONS
你想知道 MongoDB 会选择哪个计划来运行查询。
EXECUTION_STATS
您想知道您的查询是否表现良好。
QUERY_PLANNER
您的查询有问题,需要尽可能多的信息来诊断问题。

在以下示例中,我们将打印生成执行计划的聚合阶段的获胜计划的 JSON 表示形式:

data class Results (val name: String, val count: Int)
val explanation = collection.aggregate<Results>(
listOf(
Aggregates.match(Filters.eq(Restaurant::categories.name, "bakery")),
Aggregates.group("\$${Restaurant::stars.name}", Accumulators.sum("count", 1))
)
).explain(ExplainVerbosity.EXECUTION_STATS)
// Prettyprint the output
println(explanation.toJson(JsonWriterSettings.builder().indent(true).build()))
{
"explainVersion": "2",
"queryPlanner": {
// ...
},
"command": {
// ...
},
// ...
}

有关本节提及主题的更多信息,请参阅以下资源:

Kotlin 驱动程序提供了与 $group 一起使用的累加器表达式的构建器。您必须以 JSON 格式或兼容的文档格式声明所有其他表达式。

提示

以下任一示例中的语法都将定义$arrayElemAt表达式。

“类别”前面的$告诉 MongoDB 这是一个字段路径,使用输入文档中的“类别”字段。

Document("\$arrayElemAt", listOf("\$categories", 0))
// is equivalent to
Document.parse("{ \$arrayElemAt: ['\$categories', 0] }")

在以下示例中,聚合管道使用 $project 阶段和各种 Projections 来返回 name 字段和计算字段 firstCategory,其值是 categories 字段中的第一个元素。

data class Results(val name: String, val firstCategory: String)
val resultsFlow = collection.aggregate<Results>(
listOf(
Aggregates.project(
Projections.fields(
Projections.excludeId(),
Projections.include("name"),
Projections.computed(
"firstCategory",
Document("\$arrayElemAt", listOf("\$categories", 0))
)
)
)
)
)
resultsFlow.collect { println(it) }
Results(name=Sun Bakery Trattoria, firstCategory=Pizza)
Results(name=Blue Bagels Grill, firstCategory=Bagels)
Results(name=XYZ Bagels Restaurant, firstCategory=Bagels)
Results(name=Hot Bakery Cafe, firstCategory=Bakery)
Results(name=Green Feast Pizzeria, firstCategory=Pizza)
Results(name=ZZZ Pasta Buffet, firstCategory=Pasta)
Results(name=XYZ Coffee Bar, firstCategory=Coffee)
Results(name=456 Steak Restaurant, firstCategory=Steak)
Results(name=456 Cookies Shop, firstCategory=Bakery)
Results(name=XYZ Steak Buffet, firstCategory=Steak)

有关本节中提到的方法和类的详情,请参阅以下 API 文档:

后退

更新构建器

来年

聚合表达式操作