如何使用 MongoDB 聚合管道从集合中的两个子文档中返回最小值?
Posted
技术标签:
【中文标题】如何使用 MongoDB 聚合管道从集合中的两个子文档中返回最小值?【英文标题】:How can I return the minimum values from two subdocuments in a collection using MongoDB's aggregation pipeline? 【发布时间】:2015-08-26 23:09:36 【问题描述】:我们在一个数据库中有一堆产品,每个产品都有两种货币价值。每个对象都有一个制造商、一个范围和一个描述,每个对象可以有一个月租金金额(对于租赁协议)、一个月付款金额(对于财务协议)或两者兼而有之。
一个示例对象是:
"manufacturer": "Manufacturer A",
"range": "Range A",
"description": "Product Description",
"rentals":
"initialRental": 1111.05,
"monthlyRental": 123.45,
"termMonths": 24
,
"payments":
"deposit": 592.56,
"monthlyPayment": 98.76,
"finalPayment": 296.28,
"termMonths": 36
对于给定的制造商和范围,通常可以有多个对象。
我正在寻找一个聚合管道,它将返回每个不同制造商/范围对的最低月租金和最低月付款的列表,但我对如何使用聚合框架的有限知识似乎吸引了我出去。
如果有一个不同的制造商有两个不同的范围,我的预期结果将如下:
[
"manufacturer": "Manufacturer A",
"range": "Range A",
"minimumRental": 123.45,
"minimumPayment": 98.76
,
"manufacturer": "Manufacturer A",
"range": "Range B",
"minimumRental": 234.56,
"minimumPayment": 197.53
]
我正在使用以下方法来尝试实现这一目标,但我似乎在$min
的分组和使用上绊倒了:
db.products.aggregate(
[
"$group":
"_id":
"manufacturer": "$manufacturer.name",
"range": "$range.name"
,
"rentals":
"$addToSet": "$rentals.monthlyrental"
,
"payments":
"$addToSet": "$payments.monthlypayment"
,
"$group":
"_id":
"manufacturer": "$_id.manufacturer",
"range": "$_id.range",
"payments": "$payments"
,
"minimumRental":
"$min": "$rentals"
,
"$project":
"_id":
"manufacturer": "$_id.manufacturer",
"range": "$_id.range",
"minimumRental": "$minimumRental",
"payments": "$_id.payments"
,
"$group":
"_id":
"manufacturer": "$_id.manufacturer",
"range": "$_id.range",
"minimumRental": "$_id.minimumRental"
,
"minimumPayment":
"$min": "$_id.payments"
,
"$project":
"_id": 0,
"manufacturer": "$_id.manufacturer",
"range": "$_id.range",
"minimumRental": "$_id.minimumRental",
"minimumPayment": "$minimumPayment"
]
)
值得注意的是,就我的测试数据而言,我故意没有为 Range B 指定租金,因为在某些情况下,租金和/或付款都没有针对给定的范围指定。
因此,对我的测试数据使用上面的查询可以得到以下结果:
"0" :
"minimumPayment" : [
98.76
],
"manufacturer" : "Manufacturer A",
"range" : "Range A",
"minimumRental" : [
123.45
]
,
"1" :
"minimumPayment" : [
197.53
],
"manufacturer" : "Manufacturer A",
"range" : "Range B",
"minimumRental" : []
这很接近,但似乎我得到的是一个数组而不是最小值。我的印象是我正在尝试做的事情是可能的,但我似乎无法找到任何足够具体的资源来找出我做错了什么。
感谢阅读。
【问题讨论】:
【参考方案1】:这有点复杂,但这里有一点要理解。第一种情况是简化,然后为每个情况找到最小的数量
db.collection.aggregate([
// Tag things with an A/B value11
"$project":
"_id":
"manufacturer": "$manufacturer.name",
"range": "$range.name",
,
"rental": "$rentals.monthlyRental",
"payment": "$payments.monthlyPayment"
"type": "$literal": [ "R","P" ]
,
// Unwind that "type"
"$unwind": "$type" ,
// Group conditionally on the type
"$group":
"_id":
"_id": "$_id",
"type": "$type"
,
"value":
"$min":
"$cond": [
"$eq": [ "$type", "R" ] ,
"$rental",
"$payment"
]
,
// Sort by type and amount
"$sort": "_id.type": 1, "value": 1 ,
// Group by type only and just take the first after sort
"$group":
"_id": "$_id.type",
"manufacturer": "$first": "$_id._id.manufacturer" ,
"range": "$first": "$_id._id.range"
])
基本上就是这样,只需根据需要使用$project
清理字段或在代码中处理它。
虽然我个人觉得这有点草率并且由于$unwind
执行“A/B”值而产生了一些开销。更好的方法是在并行查询中运行每个聚合,然后只需合并结果以发送到客户端。
我可以整天讨论并行查询,但基本示例在我最近给出的答案中,所以请阅读How to Group By Different Fields,它已经显示了执行此操作的一般技术。
【讨论】:
以上是关于如何使用 MongoDB 聚合管道从集合中的两个子文档中返回最小值?的主要内容,如果未能解决你的问题,请参考以下文章
使用 mongodb 聚合管道按升序向集合中的所有记录添加日期