Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help needed with aggregation and pagination #212

Open
rounakcodes opened this issue Aug 27, 2022 · 8 comments
Open

Help needed with aggregation and pagination #212

rounakcodes opened this issue Aug 27, 2022 · 8 comments

Comments

@rounakcodes
Copy link

rounakcodes commented Aug 27, 2022

I am new to mongodb. This works for me:

const queryCallsByUserId = async (filter, options) => {
  const results = await Call.aggregate([
    { $lookup: { from: 'patients', localField: 'patientId', foreignField: '_id', as: 'patientDetails' } },
  ]);
  return results;
};

However, I don't get any fields related to pagination.

If I use only Call.paginate(), it returns results with pagination. But I don't know how to have paginate working after performing the above aggregation.
Thanks.

@kallyas
Copy link

kallyas commented Aug 27, 2022

Pagination is already taken care of, if you take a look at this file paginate.plugin.js

To make it work, you have to add it to your schema

const mongoose = require('mongoose');]
const { toJSON, paginate } = require('./plugins');

const fooSchema = mongoose.Schema({
...
})

// add plugin that converts mongoose to json
fooSchema.plugin(toJSON);
// add plugin for pagination
fooSchema.plugin(paginate);

In your frontend you can call the api with pagination params like so
GET http://localhost:5000/api/v1/foo?page=1&limit=10

The result will be something like this

{
limit: 10
page: 1
results: [{}...]
totalPages: 1
totalResults: 10
}

@rounakcodes
Copy link
Author

rounakcodes commented Aug 28, 2022

Yes. Correct. That is what I meant when I said "If I use only Call.paginate(), it returns results with pagination". The problem is when I use the aggregation Call.aggregate that I get results which are not paginated. How to apply pagination to results returned by Call.aggregate?

(PS: I also checked the results using callsWithAgg?page=1&limit=10 but there was no change )

@Shidooo
Copy link

Shidooo commented Sep 2, 2022

I face a similar problem (by using toJSON), I also using an aggregation function. It seems that plugins attached to a schema are not taken when calling an aggregation function.

Unfortunately, I can't find anything about this in the mongodb or mongoose documentation.

@rounakcodes
Copy link
Author

rounakcodes commented Sep 3, 2022

Not exactly pagination substitute but I got away for now doing:

const queryCalls = async (filter, { limit = 10, page = 1 }) => {
  const results = await Call.aggregate([
      { $lookup: { from: 'patients', localField: 'patientId', foreignField: '_id', as: 'patientDetails' } },
      {
        $setWindowFields: {
          output: { totalCount: { $count: {} } },
        },
      },
      { $skip: (page - 1) * limit || 0 },
      { $limit: Number(limit) },
  ]);
  return results;
}

@Shidooo Would passing your aggregate results to JSON.stringify() work?

@Shidooo
Copy link

Shidooo commented Sep 3, 2022

Glad that you found a workaround for your problem!

Yes, it works but it is not what I expect.

According to the implementation of toJSON, , it

  • removes __v, createdAt, updatedAt, and any schema path that has private: true
  • replaces _id with id

I could certainly change it manually (not very nice if you have several aggregate functions), but isn't there a more elegant solution? Since the toJSON plugin exists, It would be better to use it (instead of rewriting it just for aggregation functions...)

@Shidooo
Copy link

Shidooo commented Sep 14, 2022

My workaround, hoping to find a more elegant solution.

   // rename _id to id
    {
      $addFields: {
        id: '$_id',
      },
    },
    // remove _id and __v
    {
      $project: {
        __v: 0,
        _id: 0,
      },
    },

@sagardspeed2
Copy link

Is there any update on this ?

@kallyas
Copy link

kallyas commented Jul 20, 2023

My work around, is as follows

const queryCalls = async (filter, { limit = 10, page = 1 }) => {
  // Calculate the offset based on the current page and limit
  const offset = (page - 1) * limit;

  // Perform the aggregation pipeline
  const results = await Call.aggregate([
    { $lookup: { from: 'patients', localField: 'patientId', foreignField: '_id', as: 'patientDetails' } },
    // You can add more pipeline stages here if needed
    // ...

    // Skip the specified number of documents based on the offset
    { $skip: offset },

    // Limit the number of documents to the specified limit
    { $limit: Number(limit) },
  ]);

  // You might want to add more logic here, depending on the use case

  return results;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants