【问题标题】:mongodb multiple groups in one resultmongodb 多个组在一个结果中
【发布时间】:2014-07-07 20:20:03
【问题描述】:

我有类似的文档存储在 mongodb 中

{
 "_id":"transaction_id"
 "customer":"some customer",
 "order_date":Date('2011-01-01'),
 "delivery_date":Date('2011-01-15'),
 "amt":500.0,
 "qty":50
},
{
 "_id":"transaction_id"
 "customer":"some customer",
 "order_date":Date('2011-01-01'),
 "delivery_date":Date('2011-02-04'),
 "amt":500.0,
 "qty":50
}

我希望对订单日期和交货日期进行一些汇总,以绘制每月订购和交付给每个客户的库存总量。

当然,我可以运行 2 个聚合查询来获得我想要的,但我只是想知道是否可以通过 1 个命令获得具有 2 组组的结果?

预期结果如下:

results:[{
 _id:{
   customer:"some customer"
 },
 orders:[
  {
   year:2011,
   month:1,
   qty:100
  },
   ...
 ]
 deliveries:[
  {
   year:2011,
   month:1,
   qty:50
  },
  {
   year:2011,
   month:2,
   qty:50
  },
  ...
 ]
},...]

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    如果我理解正确的话:

    db.collName.aggregate({$project:{
                            customer:1,
                            order:{ 
                                    qty:"$qty", 
                                    year:{$year:"$order_date"}, 
                                    month:{$month:"$order_date"}
                                  },
                            delivery:{
                                     qty:"$qty", 
                                     year:{$year:"$delivery_date"}, 
                                     month:{$month:"$delivery_date"}
                                  } 
                               }
                     },
                     {$group:{
                               _id:{
                                  customer:"$customer"
                               },
                               orders: { $push:"$order" },
                               deliveries:{ $push:"$delivery"}
                              }
                     });
    

    【讨论】:

    • 很接近,但它并没有总结同年同月的数量,这是我所希望的。
    • 仅供参考,有一个您可能感兴趣的解决方案。
    【解决方案2】:

    您可以在单个查询中完成此操作,您只需要在操作文档方面有点创意,然后基本上执行 两个 $group 阶段,先按日期添加,然后按客户添加.

    首先对于当前的 MongoDB 版本 2.6 及更高版本,由于使用了一些运算符:

    db.transactions.aggregate([
    
        // Project an additional array, stands for "order", "delivery"
        { "$project": {
            "_id": 0,
            "customer": 1,
            "order_date": 1,
            "delivery_date": 1,
            "qty": 1,
            "type": { "$literal": ["o","d"] }
        }},
    
        // Unwind that array, creates two documents by "type"
        { "$unwind": "$type" },
    
        // Group by "customer", "type" and date
        { "$group": {
            "_id": { 
                "customer": "$customer",
                "type": "$type",
                "year": { 
                    "$year": { 
                        "$cond": [
                            { "$eq": [ "$type", "o" ] },
                            "$order_date",
                            "$delivery_date"
                        ]
                    }
                },
                "month": { 
                    "$month": { 
                        "$cond": [
                            { "$eq": [ "$type", "o" ] },
                            "$order_date",
                            "$delivery_date"
                        ]
                    }
                }
            },
            "qty": { "$sum": "$qty" }
        }},
    
        // Group on the "customer" selecting which array to add to
        { "$group": {
            "_id": "$_id.customer",
            "orders": {
                "$push": {
                    "$cond": [
                        { "$eq": [ "$_id.type", "o" ] },
                        {
                            "year": "$_id.year",
                            "month": "$_id.month",
                            "qty": "$qty"
                        },
                        false
                    ]
                }
            },
            "deliveries": {
                "$push": {
                    "$cond": [
                        { "$eq": [ "$_id.type", "d" ] },
                        {
                            "year": "$_id.year",
                            "month": "$_id.month",
                            "qty": "$qty"
                        },
                        false
                    ]
                }
            }
        }},
    
        // Getting rid of the `false` values in there
        { "$project": {
            "orders": { "$setDifference": [ "$orders", [false] ] },
            "deliveries": { "$setDifference": [ "$deliveries", [false] ] },
        }},
    
        // But "sets" are not considered ordered, so sort them
        { "$unwind": "$orders" },
        { "$sort": { "orders.year": 1, "orders.month": 1 } }, 
        { "$group": {
            "_id": "$_id",
            "orders": { "$push": "$orders" },
            "deliveries": { "$first": "$deliveries" }
        }},
        { "$unwind": "$deliveries" },
        { "$sort": { "deliveries.year": 1, "deliveries.month": 1 } }, 
        { "$group": {
            "_id": "$_id",
            "orders": { "$first": "$orders" },
            "deliveries": { "$push": "$deliveries" }
        }}
    )
    

    只是在 2.6 之前的版本中这样做有点不同:

    db.transactions.aggregate([
    
        // Project an additional array, stands for "order", "delivery"
        { "$project": {
            "_id": 0,
            "customer": 1,
            "order_date": 1,
            "delivery_date": 1,
            "qty": 1,
            "type": { "$cond": [ 1, ["o","d"], 0 ] }
        }},
    
        // Unwind that array, creates two documents by "type"
        { "$unwind": "$type" },
    
        // Group by "customer", "type" and date
        { "$group": {
            "_id": { 
                "customer": "$customer",
                "type": "$type",
                "year": { 
                    "$year": { 
                        "$cond": [
                            { "$eq": [ "$type", "o" ] },
                            "$order_date",
                            "$delivery_date"
                        ]
                    }
                },
                "month": { 
                    "$month": { 
                        "$cond": [
                            { "$eq": [ "$type", "o" ] },
                            "$order_date",
                            "$delivery_date"
                        ]
                    }
                }
            },
            "qty": { "$sum": "$qty" }
        }},
    
        // Group on the "customer" selecting which array to add to
        { "$group": {
            "_id": "$_id.customer",
            "orders": {
                "$push": {
                    "$cond": [
                        { "$eq": [ "$_id.type", "o" ] },
                        {
                            "year": "$_id.year",
                            "month": "$_id.month",
                            "qty": "$qty"
                        },
                        false
                    ]
                }
            },
            "deliveries": {
                "$push": {
                    "$cond": [
                        { "$eq": [ "$_id.type", "d" ] },
                        {
                            "year": "$_id.year",
                            "month": "$_id.month",
                            "qty": "$qty"
                        },
                        false
                    ]
                }
            }
        }},
    
        // Filter `false` and sort on date
        { "$unwind": "$orders" },
        { "$match": { "orders": { "$ne": false } } },
        { "$sort": { "orders.year": 1, "orders.month": 1 } }, 
        { "$group": {
            "_id": "$_id",
            "orders": { "$push": "$orders" },
            "deliveries": { "$first": "$deliveries" }
        }},
        { "$unwind": "$deliveries" },
        { "$match": { "deliveries": { "$ne": false } } },
        { "$sort": { "deliveries.year": 1, "deliveries.month": 1 } }, 
        { "$group": {
            "_id": "$_id",
            "orders": { "$first": "$orders" },
            "deliveries": { "$push": "$deliveries" }
        }}
    
    ])
    

    基本上总结一下这里的方法,你正在做的是复制每个文档并分配一个代表“订单”或“交付”的“类型”。然后,当您按“客户”和“日期”和“类型”分组时,您有条件地根据当前类型决定选择哪个“日期”,并总结该键下的“数量”。

    由于结果是每个客户的“订单”和“交付”数组,因此您可以根据当前的“类型”有条件地将该数组$push 指定为文档值或false " 文档的每个数组。

    最后,由于这些数组现在包含 false 的值以及所需的文档,因此您可以过滤掉这些值并确保您的数组处于正确的“日期”顺序(如果您确实需要)。

    是的,列表有两个以上的$group 阶段,繁重的工作实际上是在两个分组中完成的,如果您需要,其他的只是用于数组操作,但它会为您提供准确和有序的结果。

    因此,这可能不是您可能想到的第一种方法,但显示了一些有趣的转换想法,您可以将其与各种 aggregation operators 一起使用以解决问题。这是做什么的:)

    【讨论】:

      【解决方案3】:

      我遇到了类似的问题,我需要将我的结果分为多个组,并且查看所有这些答案让我头晕目眩。经过大量研究,我找到了我正在寻找的确切内容。

      MongoDB 在 3.4 版中引入了一个名为 $facet 的新命令,这使得在单个命令中包含多个组变得非常容易。看看他们的文档:

      $facet (aggregation)

      我打算在这里用文字解释这一切,但我认为他们的文档更清晰,写得更漂亮,还有很好的例子。

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 2021-07-23
        • 1970-01-01
        • 2015-02-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-08
        • 1970-01-01
        相关资源
        最近更新 更多