聚合(Aggregation)
Overview
在本指南中,您可以了解如何在 MongoDB Kotlin 驱动程序中使用聚合操作。
聚合操作处理 MongoDB 集合中的数据并返回计算结果。 MongoDB 的聚合管道是查询 API 的一部分,以数据处理管道的概念为模型。文档进入多阶段管道,该管道将文档转换为聚合结果。
可以将聚合理解为汽车工厂。 汽车工厂内有一条装配线,沿线有配备专门工具来完成特定作业的装配站,例如钻头和焊机。毛坯零件进入工厂,然后被转换并组装成成品。
聚合管道是装配线,聚合阶段是装配站,操作符表达式则是专用工具。
聚合和查找操作比较
使用 find
操作,您可以:
选择要返回哪些文档
选择要返回哪些字段
对结果进行排序
使用aggregation
操作,您可以:
执行所有
find
操作重命名字段
计算字段
汇总数据
对值进行分组
聚合操作存在一些限制,您必须牢记:
返回的文档不得违反 BSON 文档大小限制(16 兆字节)。
默认,管道阶段的内存限制为100 MB。如果需要,您可以使用 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( 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": { // ... }, // ... }
有关本节提及主题的更多信息,请参阅以下资源:
解释输出服务器手册条目
查询计划服务器手册条目
ExplainVerbosity API 文档
解释() API 文档
AggregateFlow API 文档
聚合表达式
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 文档: