1

我们在数据库中有一堆产品,每个产品都有两种货币价值。每个对象都有一个制造商、一个范围和一个描述,每个对象可以有一个月租金金额(对于租赁协议)、一个月付款金额(对于财务协议)或两者兼而有之。

一个示例对象是:

{
    "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" : []
    }
}

这很接近,但似乎我得到的是一个数组而不是最小值。我的印象是我正在尝试做的事情是可能的,但我似乎无法找到任何足够具体的资源来找出我做错了什么。

谢谢阅读。

4

1 回答 1

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”值而有点草率并且有一点开销。更好的方法是在并行查询中运行每个聚合,然后只需合并结果以发送到客户端。

我可以整天讨论并行查询,但基本示例是在我最近给出的答案中,因此请阅读如何按不同字段分组,其中显示了执行此操作的一般技术。

于 2015-06-11T10:28:52.140 回答