Aravind
Aravind

Reputation: 424

Mongodb skip and limit not working properly for pagination

I'm working on a project where huge amount of data (in lacks) being stored in the mongodb collection using mongoose. So instead of using mongoose-aggregate-paginate-v2, I'm using skip and limit for pagination.

But the pagination is not working properly. The data shown in the first page is also being shown in the second page. I think as name suggests skip just skipping the number of records and showing the remaining records in the response.

This is the query I'm using

assets.aggregate(
  [
    { $skip: 1 },
    { $match: { company: "61bf118b87b32c3ccc7a96a6" } },
    { $sort: { createdAt: -1 } },
    { $limit: 10 },
    {
      $lookup: {
        from: "tickets",
        localField: "_id",
        foreignField: "assets",
        as: "ticket_assets",
      },
    },
    {
      $lookup: {
        from: "locations",
        localField: "location",
        foreignField: "_id",
        as: "locationData",
      },
    },
    { $unwind: { path: "$locationData", preserveNullAndEmptyArrays: true } },
    {
      $lookup: {
        from: "stores",
        localField: "sublocation",
        foreignField: "_id",
        as: "sublocation",
      },
    },
    { $unwind: { path: "$sublocation", preserveNullAndEmptyArrays: true } },
    {
      $lookup: {
        from: "user_dropdowns",
        localField: "category",
        foreignField: "_id",
        as: "categoryData",
      },
    },
    { $unwind: { path: "$categoryData", preserveNullAndEmptyArrays: true } },
    {
      $lookup: {
        from: "user_dropdowns",
        localField: "type",
        foreignField: "_id",
        as: "typeData",
      },
    },
    { $unwind: { path: "$typeData", preserveNullAndEmptyArrays: true } },
    {
      $lookup: {
        from: "admin_dropdowns",
        localField: "asset_status",
        foreignField: "_id",
        as: "assetStatusData",
      },
    },
    { $unwind: { path: "$assetStatusData", preserveNullAndEmptyArrays: true } },
    {
      $lookup: {
        from: "user_dropdowns",
        localField: "condition",
        foreignField: "_id",
        as: "assetCondition",
      },
    },
    { $unwind: { path: "$assetCondition", preserveNullAndEmptyArrays: true } },
    {
      $lookup: {
        from: "stores",
        localField: "deployedAt",
        foreignField: "_id",
        as: "deployedStore",
      },
    },
    { $unwind: { path: "$deployedStore", preserveNullAndEmptyArrays: true } },
    {
      $lookup: {
        from: "company_contacts",
        localField: "assignedObj.allottedTo",
        foreignField: "_id",
        as: "assignedEmployee",
      },
    },
    {
      $unwind: { path: "$assignedEmployee", preserveNullAndEmptyArrays: true },
    },
    {
      $lookup: {
        from: "locations",
        localField: "using_location",
        foreignField: "_id",
        as: "assetUsingLocation",
      },
    },
    {
      $unwind: {
        path: "$assetUsingLocation",
        preserveNullAndEmptyArrays: true,
      },
    },
    {
      $project: {
        _id: 1,
        asset_name: 1,
        asset_code: 1,
        status: 1,
        createdAt: 1,
        assetId: 1,
        location: { $ifNull: ["$locationData", null] },
        sublocation: { $ifNull: ["$sublocation", null] },
        category: { $ifNull: ["$categoryData", null] },
        type: { $ifNull: ["$typeData", null] },
        asset_status: { $ifNull: ["$assetStatusData", null] },
        ticketCount: { $size: "$ticket_assets" },
        deployedAt: { $ifNull: ["$deployedStore", null] },
        assignedTo: {
          $ifNull: [
            {
              fullName: "$assignedEmployee.fullName",
              _id: "$assignedEmployee._id",
            },
            null,
          ],
        },
        condition: { $ifNull: ["$assetCondition", null] },
        using_location: { $ifNull: ["$assetUsingLocation", null] },
        predictive_maintenance: 1,
        maintenanceValidationData: 1,
        allocationValidationData: 1,
        purchasedFrom: 1,
        warrantyDue: 1,
        lifeTime: 1,
        purchaseDate: 1,
        asset_price: 1,
      },
    },
  ]
);

And please find the attached images of how the response being shown in the frontend table. enter image description here enter image description here

Is there any other alternative to fix the pagination in my code or where I'm making the mistake to get the correct pagination.

Upvotes: 1

Views: 3839

Answers (1)

Gaëtan Boyals
Gaëtan Boyals

Reputation: 1228

As you guessed, $skip skips X documents. It has no way to know how you manage your pages in the front-end, it might be pages of 5/10/25/50 documents.

In the request you make to your API, pass it as parameters something like this: /my/awesome/route/results?page_number=5&page_size=10.

That way, in your endpoint via req.params, you know that you have to $skip 4 pages times 10 results, so $skip: 40.


An example for this would be:

Say your URL is: https://my-api.com/data-to-retrieve?page_number=3,page_size=10.

You would do

const myApiRoute = (req, res, next) => {
  // -1 because if you want page two, you want the results from 11 to 20, and not from 21 to 30
  const pageSize = parseInt(req.query.page_size) - 1;
  const pageNumber = parseInt(req.query.page_number);
  assets.aggregate(
  [
    { $skip: pageSize * pageNumber },
    { $match: { company: "61bf118b87b32c3ccc7a96a6" } },
    { $sort: { createdAt: -1 } },
    { $limit: pageSize },
    { [...]
}

Upvotes: 1

Related Questions