如何使用 MongoDB 的聚合管道 return 集合中两个子文档的最小值?
How can I return the minimum values from two subdocuments in a collection using MongoDB's aggregation pipeline?
我们在数据库中有一堆产品,每种产品都附有两种类型的货币价值。每个对象都有一个制造商、一个范围和一个描述,每个对象可以有一个月租金额(对于租赁协议)、一个月付款金额(对于融资协议)或两者。
示例对象为:
{
"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
}
}
一个给定的制造商和范围通常可以有多个对象。
我正在寻找一个聚合管道,它将 return 每个不同 manufacturer/range 对的最低月租金和最低月付款列表,但我对如何使用的知识有限聚合框架似乎让我失望了。
如果有一个具有两个不同范围的不同制造商,我的预期结果如下:
[
{
"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"
}
}
]
)
值得注意的是,在我的测试数据中,我故意没有指定范围 B 的租金,因为在某些情况下,租金 and/or 付款并未同时指定给定范围.
因此,对我的测试数据使用上面的查询得到以下结果:
{
"0" : {
"minimumPayment" : [
98.76
],
"manufacturer" : "Manufacturer A",
"range" : "Range A",
"minimumRental" : [
123.45
]
},
"1" : {
"minimumPayment" : [
197.53
],
"manufacturer" : "Manufacturer A",
"range" : "Range B",
"minimumRental" : []
}
}
这很接近,但我得到的似乎是一个数组而不是最小值。我的印象是我尝试做的事情是可能的,但我似乎无法找到任何足够具体的资源来找出我做错了什么。
感谢阅读。
有点复杂,但这里有一点需要理解。第一种情况是简化,然后只为每个
找到最小的数量
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" 值而产生了一些开销。更好的方法是 运行 并行查询中的每个聚合,然后合并结果发送给客户端。
我可以整天讨论并行查询,但基本示例在我最近给出的答案中,所以请阅读 ,它已经展示了执行此操作的一般技术。
我们在数据库中有一堆产品,每种产品都附有两种类型的货币价值。每个对象都有一个制造商、一个范围和一个描述,每个对象可以有一个月租金额(对于租赁协议)、一个月付款金额(对于融资协议)或两者。
示例对象为:
{
"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
}
}
一个给定的制造商和范围通常可以有多个对象。
我正在寻找一个聚合管道,它将 return 每个不同 manufacturer/range 对的最低月租金和最低月付款列表,但我对如何使用的知识有限聚合框架似乎让我失望了。
如果有一个具有两个不同范围的不同制造商,我的预期结果如下:
[
{
"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"
}
}
]
)
值得注意的是,在我的测试数据中,我故意没有指定范围 B 的租金,因为在某些情况下,租金 and/or 付款并未同时指定给定范围.
因此,对我的测试数据使用上面的查询得到以下结果:
{
"0" : {
"minimumPayment" : [
98.76
],
"manufacturer" : "Manufacturer A",
"range" : "Range A",
"minimumRental" : [
123.45
]
},
"1" : {
"minimumPayment" : [
197.53
],
"manufacturer" : "Manufacturer A",
"range" : "Range B",
"minimumRental" : []
}
}
这很接近,但我得到的似乎是一个数组而不是最小值。我的印象是我尝试做的事情是可能的,但我似乎无法找到任何足够具体的资源来找出我做错了什么。
感谢阅读。
有点复杂,但这里有一点需要理解。第一种情况是简化,然后只为每个
找到最小的数量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" 值而产生了一些开销。更好的方法是 运行 并行查询中的每个聚合,然后合并结果发送给客户端。
我可以整天讨论并行查询,但基本示例在我最近给出的答案中,所以请阅读