聚合
概述
在本指南中,您可以了解如何在 MongoDB Kotlin 驱动程序中使用 聚合操作。
聚合操作处理 MongoDB 集合中的数据并返回计算结果。MongoDB 的聚合管道是查询 API 的一部分,基于数据处理管道的概念。文档进入一个多阶段管道,将文档转换为聚合结果。
将聚合的概念比作一个汽车工厂。在汽车工厂中有一条装配线,沿线上有专门的装配站,用于完成特定的工作,例如钻孔机和焊接机。原始部件进入工厂,随后被转换和组装成成品。
聚合管道是装配线,聚合阶段是装配站,运算符表达式是专用工具。
聚合与查找操作比较
使用find
操作,您可以
选择要返回的文档
选择要返回的字段
对结果进行排序
使用aggregation
操作,您可以
执行所有
find
操作重命名字段
计算字段
总结数据
分组值
聚合操作有一些限制您必须牢记
返回的文档不得违反16兆字节的BSON文档大小限制。
默认情况下,管道阶段具有100兆字节的内存限制。如果需要,您可以通过使用allowDiskUse方法来超出此限制。
有用的参考
示例数据
以下示例使用MongoDB中以下数据集
[ {"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"]} ]
该集合中的数据由以下 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( 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()
方法传递详尽程度来指定解释的详细程度。
以下表格显示了所有解释详尽程度及其预期用例
详尽程度级别 | 用例 |
---|---|
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": { // ... }, // ... }
有关本节中提到的主题的更多信息,请参阅以下资源
解释输出 服务器手册条目
查询计划 服务器手册条目
ExplainVerbosity API 文档
explain() API 文档
AggregateFlow API 文档
聚合表达式
Kotlin 驱动为与 $group
一起使用提供了累加器表达式的构建器。您必须以 JSON 格式或兼容的文档格式声明所有其他表达式。
提示
以下任一示例中的语法将定义一个 $arrayElemAt 表达式。
在 "categories" 前面的 $
告诉 MongoDB 这是一个 字段路径,使用输入文档中的 "categories" 字段。
Document("\$arrayElemAt", listOf("\$categories", 0)) // is equivalent to Document.parse("{ \$arrayElemAt: ['\$categories', 0] }")
在以下示例中,聚合管道使用 $project
阶段和多种 投影
来返回 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 文档