1

I have an aggregation pipeline that returns Regions and their respective countries with Sales value. Now I want top 5 countries by sales in each region so I'm using $slice but it returns first 5 countries without any order. How do I sort the Countries array before slicing it ? Elements of Countries array look like this:

{name:"USA",sales:999}

The project query looks like this:

    "$project": {
       "Region":1,
       "TotalSales":1,
       "Countries": {
         "$slice": [
           "$Countries",
           5
         ]
       }
    }

MongoDB v5.0

sandeep.kgp
  • 847
  • 2
  • 10

1 Answers1

1

assuming a dummy record looks like below

{
    "_id": 1,
    "Region": "North America",
    "TotalSales": 200000,
    "Countries": [
      {
        "name": "USA",
        "sales": 150000
      },
      {
        "name": "Canada",
        "sales": 50000
      },
      {
        "name": "Mexico",
        "sales": 10000
      },
      {
        "name": "Greenland",
        "sales": 5000
      },
      {
        "name": "Cuba",
        "sales": 2000
      },
      {
        "name": "Bahamas",
        "sales": 1000
      },
      {
        "name": "Jamaica",
        "sales": 500
      },
      {
        "name": "Haiti",
        "sales": 200
      },
      {
        "name": "Dominican Republic",
        "sales": 100
      }
    ]
  }

and that you want to sort by country sales field you can use the $sortArray operator (version 5.2 onwards) inside $slice in the $project stage

db.collection.aggregate([
  {
    $project: {
      Region: 1,
      TotalSales: 1,
      Countries: {
        $slice: [
          {
            $sortArray: {
              input: "$Countries",
              sortBy: {
                sales: 1
              },    
            }
          },
          5
        ]
      }
    }
  }
])

playground

if array doesn't have fields (something like ["UK","USA",..]) use

              input: "$Countries",
              sortBy: 1 //direction of sort

for versions 5.1 and below $sortArray doesn't work and you will have to go for a $unwind, $sort, $group, $push combo. see here

the query then will look like

db.collection.aggregate([
  {
    $unwind: "$Countries"
  },
  {
    $sort: {
      "Countries.sales": 1
    }
  },
  {
    $group: {
      _id: "$_id",
      Countries: {
        $push: "$Countries"
      },
      Region: {
        $first: "$Region"
      },
      TotalSales: {
        $first: "$TotalSales"
      }
    }
  },
  {
    $project: {
      Region: 1,
      TotalSales: 1,
      Countries: {
        $slice: [
          "$Countries",
          5
        ]
      }
    }
  }
])

playground

cmgchess
  • 7,996
  • 37
  • 44
  • 62