node.jsmongodbmongoosemongoose-populate

Mongoose Aggregate to group by brand_id and orders_sum > threshold stored in another collections


Sample collection of Sales looks like this:

[
  {
    "brand_id": "A",
    "price": 500
  },
  {
    "brand_id": "A",
    "price": 700
  },
  {
    "brand_id": "B",
    "price": 1500
  },
  {
    "brand_id": "C",
    "price": 100
  },
  {
    "brand_id": "D",
    "price": 400
  },
  {
    "brand_id": "D",
    "price": 600
  },
  {
    "brand_id": "D",
    "price": 200
  }
]

This is my current solution:

    const data = await Sales.aggregate([
      {
        $group: {
          _id: "$brand_id",
          total_sales: {
            $sum: "$price"
          },
          records: {
            $push: "$$ROOT"
          }
        }
      },
      {
        $match: {
          total_sales: {
            $gt: 1000
          }
        }
      },
      {
        $unwind: "$records"
      },
      {
        $replaceWith: "$records"
      }
    ])

What am trying to do is, from the Sales collection, I am trying to get all the documents grouped by brand_id and each brands total order value to be greater than 1000$ (threshold value).

But I have another collection named Brands where it contains the brand_id, brand_name and threshold value. So it would be like this:

[
  {
    brand_id:"A",
    brand_name: "abc",
    threshold: 1000,
  },
  {
    brand_id: "B",
    brand_name: "hef",
    threshold: 600,
  },
  {
    brand_id: "C",
    brand_name: "xyz",
    threshold: 310,
  }
]

The thing is, I can iterate through the documents of Brands and pass the threshold value to the aggregate function and call it each time. But it doesn't seem to be the proper approach.

I tried googling for a similar scenario. But couldn't find any.

What am expecting as output is something similar to this:

[
  "A":[
   {
     "_id": ObjectId("5a934e000102030405000000"),
     "brand_id": "A",
     "price": 500
   },
   {
     "_id": ObjectId("5a934e000102030405000001"),
     "brand_id": "A",
     "price": 700
   },
  ],
  "B":[
   {
     "_id": ObjectId("5a934e000102030405000002"),
     "brand_id": "B",
     "price": 1500
   },
  ],
  "D":[
   {
     "_id": ObjectId("5a934e000102030405000004"),
     "brand_id": "D",
     "price": 400
   },
   {
     "_id": ObjectId("5a934e000102030405000005"),
     "brand_id": "D",
     "price": 600
   },
   {
     "_id": ObjectId("5a934e000102030405000006"),
     "brand_id": "D",
     "price": 200
   }
  ]
]

Solution

  • I would suggest you start the $lookup from the Brands collection to look up all the sales record. Use $sum on the $lookup result to get the total sales amount, then $match with the threshold value

    db.brands.aggregate([
      {
        "$lookup": {
          "from": "sales",
          "localField": "brand_id",
          "foreignField": "brand_id",
          "as": "salesLookup"
        }
      },
      {
        "$set": {
          "total_sales": {
            $sum: "$salesLookup.price"
          }
        }
      },
      {
        "$match": {
          $expr: {
            $gte: [
              "$total_sales",
              "$threshold"
            ]
          }
        }
      }
    ])
    

    Mongo Playground