对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。

聚合(Aggregation)

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

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

提示

完成聚合教程

您可以在服务器手册的完整聚合管道教程部分找到详细解释常见聚合任务的教程。选择一个教程,然后从页面右上角的 Select your language 下拉菜单中选择 Kotlin (Coroutine)。

聚合的功能类似于汽车工厂中的功能。汽车工厂内有一条装配线,沿线有配备专门工具来完成特定作业的装配站,例如钻头和焊机。毛坯零件进入工厂,工厂将其转换并组装成成品。

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

使用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驱动程序提供了 Aggregates 辅助类,其中包含聚合阶段的构建者。

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

  • 使用 $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 outputprintln(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 文档: