在一个结果中mongodb多个组

时间:2014-05-19 18:51:46

标签: mongodb mongodb-query aggregation-framework

我有类似于存储在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组命令的结果?

预期结果如下:

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
  },
  ...
 ]
},...]

3 个答案:

答案 0 :(得分:5)

您可以在一个查询中执行此操作,您只需要有点创意操作文档,然后基本上执行两个 $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来解决问题。这是做什么:)

答案 1 :(得分:0)

如果我理解正确的话:

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 :(得分:0)

我遇到了类似的问题,我需要将我的结果分为多个组,然后查看所有这些答案,这让我很头疼。经过大量研究后,我找到了我想要的确切东西。

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

$facet (aggregation)

我将在此处用文字解释所有内容,但我认为他们的文档更加清晰明了,并带有很好的示例。

希望有帮助。