Search code examples
mongodbmongoose

Paginate MongoDB aggregation response


I have a database of users that have skills. I have set up a way to find users in the database using am aggregation method included in mongoose. Depending on the search criteria I input into the aggregation, the results may be too big to actually display on my front end app. I am curious how I can paginate an aggregation query with the typical limit, page, and skip variables like you would do in a typical GET request.

Here is my aggregation query:

const foundUsers = await User.aggregate([
{
  $addFields: {
    matchingSkills: {
      $filter: {
        input: '$skills',
        cond: {
          $or: test,
        },
      },
    },
    requiredSkills,
  },
},
{
  $addFields: {
    // matchingSkills: '$$REMOVE',
    percentageMatch: {
      $multiply: [
        { $divide: [{ $size: '$matchingSkills' }, skillSearch.length] }, // yu already know how many values you need to pass, thats' why `2`
        100,
      ],
    },
  },
},
{
  $addFields: {
    matchingSkillsNames: {
      $map: {
        input: '$matchingSkills',
        as: 'matchingSkill',
        in: '$$matchingSkill.skill',
      },
    },
  },
},
{
  $addFields: {
    missingSkills: {
      $filter: {
        input: '$requiredSkills',
        cond: {
          $not: {
            $in: ['$$this', '$matchingSkillsNames'],
          },
        },
      },
    },
  },
},
{
  $match: { percentageMatch: { $gte: 25 } },
},
]);

Passing these skills to this aggregate function:

{
"skillSearch": [
    {
      "class": "skills",
      "skill": "SQL",
       "operator": "GT",
  "yearsExperience": 6
    },
 {
  "class": "skills",
  "skill": "C",
  "operator": "GT",
  "yearsExperience": 1
}
  ]
}

Will result in a response similar to this:

 {
  "_id": "60184ce81e65633873d709aa",
  "name": "Brad",
  "email": "[email protected]",
  "password": "$2a$12$37v2RwaO5LhSMT8GJQSZyel.Aawn6AmlqqSOkZtopqIIXyJ0LRBfu",
  "__v": 0,
  "skills": [
      {
          "_id": "60a306ce819cde701c1934a8",
          "skill": "SQL",
          "yearsExperience": 8
      },
      {
          "_id": "60a306ce819cde701c1934a9",
          "skill": "C",
          "yearsExperience": 5
      },
      {
          "_id": "60a306ce819cde701c1934aa",
          "skill": "PL/I",
          "yearsExperience": 2
      },
      {
          "_id": "60a306ce819cde701c1934ab",
          "skill": "Awk",
          "yearsExperience": 9
      }
  ],
  "matchingSkills": [
      {
          "_id": "60a306ce819cde701c1934a8",
          "skill": "SQL",
          "yearsExperience": 8
      },
      {
          "_id": "60a306ce819cde701c1934a9",
          "skill": "C",
          "yearsExperience": 5
      }
  ],
  "requiredSkills": [
      "SQL",
      "C"
  ],
  "percentageMatch": 100,
  "matchingSkillsNames": [
      "SQL",
      "C"
  ],
  "missingSkills": []
},

Solution

  • For the pagination, you need to pass the page and size form the front end

    • $sort to sort the documents,
    • $skip skip the documents. For eg : if you are in page two and u need 10 rows , u need to skip first 10 documents
    • $limit to how many documents you need to show after skip

    here is the code

    db.collection.aggregate([
      {
        $sort: {
          _id: 1
        }
      },
      {
        $skip: 0  // page*size
      },
      {
        $limit: 10  // size
      }
    ])
    

    Working Mongo playground

    More than, the pagination requires total elements too, for that

    db.collection.aggregate([
      {
        "$facet": {
          "elements": [
            {
              "$group": {
                "_id": null,
                "count": { "$sum": 1 }
              }
            }
          ],
          "data": [
            { $sort: { _id: 1 } },
            { $skip: 0 }, // page*size 
            { $limit: 10 } // size
          ]
        }
      },
      { "$unwind": "$elements" },
      {
        "$addFields": {
          "elements": "$$REMOVE",
          "totalRecords": "$elements.count"
        }
      }
    ])
    

    Working Mongo playground