13

I have a json data which contains many objects. I want to limit the data for pagination and I need the total items count. Please help.

Model.find().skip((pageNumber-1)*limit).limit(limit).exec()

I want the count and skipped data in response.

Ashh
  • 44,693
  • 14
  • 105
  • 132
govind
  • 163
  • 1
  • 1
  • 11
  • 1
    Possible duplicate of [Mongoose limit/offset and count query](http://stackoverflow.com/questions/13935733/mongoose-limit-offset-and-count-query) – chridam Jan 22 '16 at 11:34
  • 1
    Possible duplicate of [Get a count of total documents with MongoDB when using limit](https://stackoverflow.com/a/53220591/7510657) – Ashh Nov 09 '18 at 06:03

5 Answers5

15

You can use async library for running 2 queries at once. In your case you can run one query to get the number of documents and another for pagination.

Example with 'User' model:

    var async = require('async');
    var User = require('./models/user');

    var countQuery = function(callback){
         User.count({}, function(err, count){
               if(err){ callback(err, null) }
               else{
                   callback(null, count);
                }
         }
    };

    var retrieveQuery = function(callback){
        User.find({}).skip((page-1)*PAGE_LIMIT)
                     .limit(PAGE_LIMIT)
                     .exec(function(err, doc){
                            if(err){ callback(err, null) }
                            else{
                            callback(null, doc);
                            }
                      }
    };

    async.parallel([countQuery, retrieveQuery], function(err, results){
         //err contains the array of error of all the functions
         //results contains an array of all the results
         //results[0] will contain value of doc.length from countQuery function
         //results[1] will contain doc of retrieveQuery function
         //You can send the results as

         res.json({users: results[1], pageLimit: PAGE_LIMIT, page: page, totalCount: results[0]});

    });

async allows you to run a number of queries in parallel depending on the hardware you are using. This would be faster than using 2 independent queries to get count and get the required documents. Hope this helps.

Jibin Mathews
  • 606
  • 4
  • 13
10

I have solved it with $facet and aggregate the following way in mongoose v3+:

const [{ paginatedResult, [{ totalCount }] }] = await Model.aggregate([{
  $facet: {
    paginatedResult: [
      { $match: query },
      { $skip: skip },
      { $limit: limit }
    ],
    totalCount: [
      { $match: query },
      { $count: 'totalCount' }
    ]
  }
}])

where the totalCount refers the total number of records matching the search query while the paginatedResult is only the paginated slice of them.

gazdagergo
  • 6,187
  • 1
  • 31
  • 45
5

The problem with these solutions is that for every request you are doing two queries. This becomes problematic when you have a complex data structure and large data set as performance becomes an issue. Consider instead creating a special function that listens for the /resource?count=true or /resource/count GET methods and returns only the count.

Pbrain19
  • 1,975
  • 2
  • 14
  • 11
4

You need to perform 2 queries to achieve that. One to get results and another to get total items amount with .count().

For example code you can watch at on of "paginator" for mongoose mongoose-paginate.

Alexey B.
  • 11,965
  • 2
  • 49
  • 73
-2

To performe only one query, you may use the find() method associated with promises and array slices. A small example would be:

getPaginated(query, skip, limit){
       return this.model.find(query)
            .lean()
            .then((value)=>{
                if (value.length === 0) return {userMessage: 'Document not found'};
                const count = value.length;

                //skip===0 must be handled
                const start = parseInt(limit)*parseInt(skip - 1);
                const end = start + parseInt(reqQuery.pagesize);

                //slicing the array
                value = value.slice(start,end);

                //could return it another way...
                value.push( { 'querySize': count });
                return value;
              })
              .catch((reason)=>{
                //...handling code
              });
          }