【问题标题】:MongoDB : Select top N records based on group by clauseMongoDB:根据group by子句选择前N条记录
【发布时间】:2020-05-27 19:17:23
【问题描述】:

以下是我的样本集:-

{
"_id" : "fffdb22b-912d-4166-909b-7d0fe790ba2a", 
"companyVendorId" : "1001", 
"date" : ISODate("2019-01-02T05:30:00.000+05:30"),  
"amount" : 100  
},
{
"_id" : "fff94874-27af-4a39-ae59-3a46b8c7f573", 
"companyVendorId" : "1002", 
"date" : ISODate("2019-01-25T05:30:00.000+05:30"),  
"amount" : 200  
 },
{
"_id" : "fff94874-27af-4a39-ae59-3a46b8c7f573", 
"companyVendorId" : "1002", 
"date" : ISODate("2019-01-29T05:30:00.000+05:30"),  
"amount" : 200  
 },

{
"_id" : "fff68faf-2f11-480f-83d2-bfcb45b12d5b", 
"companyVendorId" : "1004", 
"date" : ISODate("2019-01-12T05:30:00.000+05:30"),  
"amount" : 500

 },

{
"_id" : "fff4dfaa-46cd-48e3-a871-1f086a2c5438", 
"companyVendorId" : "1005", 
"date" : ISODate("2019-02-13T05:30:00.000+05:30"),  
"amount" :600

 },

 {
"_id" : "fff18ff2-015e-4ddc-81f2-a12ab3503d05", 
"companyVendorId" : "1006", 
"date" : ISODate("2019-02-08T05:30:00.000+05:30"),  
"amount" : 700

},
{
"_id" : "ffeb16cd-ae1b-4c1e-aa93-d64347b5ff38", 
"companyVendorId" : "1007", 
"date" : ISODate("2019-02-18T05:30:00.000+05:30"),  
"amount" :800
}

要求 :- 我需要每个月(本年度)的前 2 个供应商,根据他们的数量,如果一个月内有多个相同的“companyVendorId”交易,那么我们需要显示金额总和。

预期结果:-

{
"month":1
"companyVendorId" : "1004",     
"totalAmount" : 500 
 },
{
 "month":1
"companyVendorId" : "1002",     
"totalAmount" : 400

 },
{
"month":2
"companyVendorId" : "1007",         
"totalAmount" :800
 },

{
"month":2   
"companyVendorId" : "1006",     
"totalAmount" : 700

}

到目前为止,我正处于尝试进行查询之间,下面的查询我能够进行:-

 db.getCollection('transaction').aggregate([
 {
"$project":
 {
 "amount": 1,
 "companyVendorId":1,

 "month": { "$month": "$date" }, "year": { "$year": "$date" }

 }
 },
 {
 "$match":
   {
    "year": 2020
   }

  }, {
 "$group": {
    "_id": {
    "month": "$month",
   "companyVendorId":"$companyVendorId"
    },
   "totalAmount": { "$sum": "$amount" }
     }
     }
    ])

此查询根据我的要求给出结果,但无法选择前 2 名供应商。

【问题讨论】:

  • 请包含与您的输入相匹配的预期输出
  • 如果同一个 companyVendorId 的总和大于 top companyVendorId 会发生什么?示例:companyVendorId" : "1002", "totalAmount" : 501
  • @Valijon 所以在这种情况下 companyVendorId" : "1002" 记录将首先出现。
  • @thammada 我不确定你在问什么,我已经在我的问题中添加了预期的输出,并且它也与输入匹配,在预期的输出“月”字段中显示该记录用于哪个月份和“companyVendorId”已经在我的集合中,并且“totalAmount”字段指定了“amount”字段的总数。所以就我而言,我的问题是正确的。
  • @HarishBagora 抱歉,我错过了那部分。我可能以某种方式滚动过去。并认为这是输入。

标签: mongodb aggregation-framework


【解决方案1】:

有点complicated。第 7 阶段和第 8 阶段是最复杂的部分,因为您需要汇总具有相同 ID 的供应商。如果 top 2...n 供应商相同,我们需要将它们全部相加并将其计为供应商Nº1。供应商Nº2 的流程相同。

说明

  1. Stage 1. 如果要应用MongoDB索引,我们可以通过date字段过滤记录。如果没有索引,您可以用我的替换您的 2 个阶段。
  2. 第 2 阶段。我们按 month 分组并创建两个具有 companyVendorIdamount 值的数组。
  3. 3-4 阶段。我们将data2 数组展平以计算$max / $min 每个companyVendorId 的数量。这将有助于我们总结具有相同 companyVendorId 的顶级供应商。
  4. 第 5-6 阶段。现在我们订购具有最高 amount 的供应商,并定义具有 $max / $min 数量的唯一供应商列表。
  5. 第 7 阶段。在这里我们修复供应商 min 数量。由于$group 取最小值,我们将其替换为下一个供应商max 值:
    {companyVendorId:1, max:500, min:50}, ---\ {companyVendorId:1, max:500, min:200}, {companyVendorId:2, max:400, min:200} ---/ {companyVendorId:2, max:400, min:200}

  6. 第 8 阶段。现在我们在 minmax 值之间对具有相同 companyVendorIdamount 的供应商求和。

  7. 9-10-11 阶段。我们将之前的结果展平,转换为所需的输出并对文档进行排序。

db.transaction.aggregate([
  {
    "$match": {
      "date": {
        "$gte": ISODate("2019-01-01T00:00:00Z"),
        "$lte": ISODate("2019-12-31T23:59:59Z")
      }
    }
  },
  {
    "$group": {
      "_id": {
        "$month": "$date"
      },
      "data": {
        "$push": {
          "_id": {
            "$month": "$date"
          },
          "companyVendorId": "$companyVendorId",
          "amount": "$amount"
        }
      },
      "data2": {
        "$push": {
          "companyVendorId": "$companyVendorId",
          "amount": "$amount"
        }
      }
    }
  },
  {
    "$unwind": "$data2"
  },
  {
    "$group": {
      "_id": {
        "month": "$_id",
        "companyVendorId": "$data2.companyVendorId"
      },
      "data": {
        "$first": "$data"
      },
      "max": {
        "$max": "$data2.amount"
      },
      "min": {
        "$min": "$data2.amount"
      }
    }
  },
  {
    "$sort": {
      "_id.month": 1,
      "max": -1
    }
  },
  {
    "$group": {
      "_id": "$_id.month",
      "data": {
        "$first": "$data"
      },
      "data2": {
        "$push": {
          "companyVendorId": "$_id.companyVendorId",
          "max": "$max",
          "min": "$min"
        }
      }
    }
  },
  {
    $addFields: {
      data2: {
        $map: {
          input: {
            $range: [
              0,
              {
                $min: [
                  {
                    $size: "$data2"
                  },
                  2
                ]
              },
              1
            ]
          },
          as: "i",
          in: {
            companyVendorId: {
              $arrayElemAt: [
                "$data2.companyVendorId",
                "$$i"
              ]
            },
            max: {
              $arrayElemAt: [
                "$data2.max",
                "$$i"
              ]
            },
            min: {
              $ifNull: [
                {
                  $arrayElemAt: [
                    "$data2.max",
                    {
                      $add: [
                        "$$i",
                        1
                      ]
                    }
                  ]
                },
                {
                  $arrayElemAt: [
                    "$data2.min",
                    "$$i"
                  ]
                }
              ]
            }
          }
        }
      }
    }
  },
  {
    $addFields: {
      data3: {
        $map: {
          input: {
            $slice: [
              "$data2",
              2
            ]
          },
          as: "topN",
          in: {
            $reduce: {
              input: "$data",
              initialValue: {
                _id: "",
                companyVendorId: "$$topN.companyVendorId",
                amount: 0
              },
              in: {
                _id: "$_id",
                companyVendorId: "$$value.companyVendorId",
                amount: {
                  $add: [
                    "$$value.amount",
                    {
                      $cond: [
                        {
                          $and: [
                            {
                              $eq: [
                                "$$value.companyVendorId",
                                "$$this.companyVendorId"
                              ]
                            },
                            {
                              $gte: [
                                "$$this.amount",
                                "$$topN.min"
                              ]
                            },
                            {
                              $lte: [
                                "$$this.amount",
                                "$$topN.max"
                              ]
                            }
                          ]
                        },
                        "$$this.amount",
                        0
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  {
    $unwind: "$data3"
  },
  {
    $replaceWith: "$data3"
  },
  {
    $sort: {
      _id: 1,
      amount: -1
    }
  }
])

MongoPlayground

注意:您需要 MongoDB v4.2。对于早期版本 (>v3.4),请替换:

{ $replaceWith: "$data3" }
         with 
{ $replaceRoot: {newRoot: "$data3" }}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-15
    • 2020-01-01
    • 2023-03-17
    • 1970-01-01
    • 2019-07-01
    • 2016-06-29
    • 2023-03-28
    • 1970-01-01
    相关资源
    最近更新 更多