MongoDB聚合管道分组

4

我正在尝试构建一个管道,根据特定的条件搜索文档,并分组某些字段以获得所需的输出。 deals 的文档结构如下:

{
   "_id":"123",
   "status":"New",
   "deal_amount":"5200",
   "deal_date":"2018-03-05",
   "data_source":"API",
   "deal_type":"New Business",
   "account_id":"A1"
},
{
   "_id":"456",
   "status":"New",
   "deal_amount":"770",
   "deal_date":"2018-02-11",
   "data_source":"API",
   "deal_type":"New Business",
   "account_id":"A2"
},
{
   "_id":"885",
   "status":"Old",
   "deal_amount":"4070",
   "deal_date":"2017-09-22",
   "data_source":"API",
   "deal_type":"New Business",
   "account_id":"A2"
},

账户名称是一个被引用的字段。账户文档如下:

{
   "_id":"A1",
   "name":"Sarah",
},
{
   "_id":"A2",
   "name":"Amber",
},

该管道应搜索“状态”为“新”的文档,且“交易金额”大于2000,并按“账户名称”分组。我使用的管道如下:

db.deal.aggregate([{
        $match: {
            status: New,
            deal_amount: {
                $gte: 2000,
            }
        }
    }, {
        $group: {
            _id: "$account_name",
        }
    },{
        $lookup:{
            from:"accounts",
            localField:"account_id",
            foreignField:"_id",
            as:"acc",
        }
    }
])

我希望结果中只显示交易金额、交易类型、交易日期和账户名称。

期望结果:

{
    "_id": "123",
    "deal_amount": "5200",
    "deal_date": "2018-03-05",
    "deal_type": "New Business",
    "account_name": "Sarah"
}, {
    "_id": "885",
    "deal_amount": "4070",
    "deal_date": "2017-09-22",
    "deal_type": "New Business",
    "account_name": "Amber"
},

我需要在“分组”阶段包含所有这些字段:deal_amount,deal_type,deal_date和account name,才能在结果中显示吗?还是有其他方法可以做到。任何帮助都将不胜感激。

4个回答

1
请使用以下查询。
aggregate([{
        $match: {
            status: "New",
            deal_amount: {
                $gte: 2000,
            }
        }
    },
    {
        $lookup:{
            from:"accounts",
            localField:"account_id",
            foreignField:"_id",
            as:"acc",
        }
    },
    {
        $unwind: {
          path: '$acc',
          preserveNullAndEmptyArrays: true,
        },
      },
      {
      $group: {
            _id: "$acc._id",
            deal_amount: { $first: '$deal_amount' },
            deal_date: { $first: '$deal_date' },
            deal_type: { $first: '$deal_type' },

        }
    }
])

你可以通过以下方式实现:

1)使用$$ROOT引用:link

 { $group : {
            _id : "$author",
            data: { $push : "$$ROOT" }
        }}

2) 通过分配单个参数

{
        $group: {
            _id: "$account_name",
            deal_amount: { $first: '$deal_amount' },
            deal_date: { $first: '$deal_date' },
            .
            .
        }
    }

0
首先,你的$gte运算符不能用于字符串字段deal_amount,所以你可能需要将该字段更改为整数或类似的类型:
// Convert String to Integer
db.deals.find().forEach(function(data) {
    db.deals.update(
            {_id:data._id},
            {$set:{deal_amount:parseInt(data.deal_amount)}});

然后,为了获取您所需的字段,使用$project重新塑造文档:
db.deals.aggregate([{
        $match: {
            "status": "New",
            "deal_amount" : {
                "$gte" : 2000 
            }
        }
    },     
     {
         $lookup:{
            from:"accounts",
            localField:"account_id",
            foreignField:"_id",
            as:"acc",
        }
    },
     {
        $project: {
            _id: 1, 
            deal_amount: 1, 
            deal_type: 1, 
            deal_date: 1, 
            "account_name": {"$let":{"vars":{"accl":{"$arrayElemAt":["$acc", 0]}}, in:"$$accl.name"}}
        }
    }    
]);

对我来说,这产生了:

{ 
    "_id" : "123", 
    "deal_amount" : 5200.0, 
    "deal_date" : "2018-03-05", 
    "deal_type" : "New Business", 
    "account_name" : "Sarah"
}

0

db.deal.aggregate([{$match: {status: {$eq: 'New'}, deal_amount: {$gte: '2000'}}}, {$group: {_id: {accountName: '$account_id', type: '$deal_type', 'amount': '$deal_amount'}}}])

数据库.交易.聚合([{$匹配: {状态: {$等于: '新'}, 交易金额: {$大于等于: '2000'}}}, {$分组: {_id: {账户名: '$账户_id', 类型: '$交易类型', '数量': '$交易金额'}}}])


2
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community
1
虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。您可以在帮助中心找到有关编写良好答案的更多信息:https://stackoverflow.com/help/how-to-answer。祝你好运! - nima
另外,您能否整理一下代码格式,以便更容易阅读您的解决方案?可以参考其他答案中的好例子。 - Jeremy Caney

0

不确定为什么需要$group阶段。您只需要添加$project阶段,以从引用集合中输出帐户名称。

{
  "$project": {
    "deal_amount": 1, 
    "deal_type": 1, 
    "deal_date": 1, 
    "account_name": {"$let":{"vars":{"accl":{"$arrayElemAt":["$acc", 0]}}, in:"$$accl.name}}
   }
}

你建议在管道中按照匹配、投影和查找阶段的顺序进行。 - Ranabir Ray
谢谢Veeram,但是这个优先顺序的原因是什么? - Ranabir Ray
Np. 匹配总是第一位。项目引用查找字段,因此查找在项目之前。 - s7vr
假设在我的情况下,如果我想要进行分组,顺序将是 $match、$group 然后是 $lookup。 - Ranabir Ray
取决于组中使用的字段。如果您不使用外部集合中的字段,则可以在查找之前执行此操作,否则请在查找之后执行此操作。 - s7vr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接