Limiting results in MongoDB but still getting the full count?

后端 未结 5 415
不思量自难忘°
不思量自难忘° 2020-12-03 09:38

For speed, I\'d like to limit a query to 10 results

db.collection.find( ... ).limit(10)

However, I\'d also like to know the total count, so

相关标签:
5条回答
  • 2020-12-03 10:12

    cursor.count() should ignore cursor.skip() and cursor.limit() by default.

    Source: http://docs.mongodb.org/manual/reference/method/cursor.count/#cursor.count

    0 讨论(0)
  • 2020-12-03 10:17

    There is a solution using push and slice: https://stackoverflow.com/a/39784851/4752635

    I prefe

    1. First for filtering and then grouping by ID to get number of filtered elements. Do not filter here, it is unnecessary.
    2. Second query which filters, sorts and paginates.

    Solution with pushing $$ROOT and using $slice runs into document memory limitation of 16MB for large collections. Also, for large collections two queries together seem to run faster than the one with $$ROOT pushing. You can run them in parallel as well, so you are limited only by the slower of the two queries (probably the one which sorts).

    I have settled with this solution using 2 queries and aggregation framework (note - I use node.js in this example, but idea is the same):

    var aggregation = [
      {
        // If you can match fields at the begining, match as many as early as possible.
        $match: {...}
      },
      {
        // Projection.
        $project: {...}
      },
      {
        // Some things you can match only after projection or grouping, so do it now.
        $match: {...}
      }
    ];
    
    
    // Copy filtering elements from the pipeline - this is the same for both counting number of fileter elements and for pagination queries.
    var aggregationPaginated = aggregation.slice(0);
    
    // Count filtered elements.
    aggregation.push(
      {
        $group: {
          _id: null,
          count: { $sum: 1 }
        }
      }
    );
    
    // Sort in pagination query.
    aggregationPaginated.push(
      {
        $sort: sorting
      }
    );
    
    // Paginate.
    aggregationPaginated.push(
      {
        $limit: skip + length
      },
      {
        $skip: skip
      }
    );
    
    // I use mongoose.
    
    // Get total count.
    model.count(function(errCount, totalCount) {
      // Count filtered.
      model.aggregate(aggregation)
      .allowDiskUse(true)
      .exec(
      function(errFind, documents) {
        if (errFind) {
          // Errors.
          res.status(503);
          return res.json({
            'success': false,
            'response': 'err_counting'
          });
        }
        else {
          // Number of filtered elements.
          var numFiltered = documents[0].count;
    
          // Filter, sort and pagiante.
          model.request.aggregate(aggregationPaginated)
          .allowDiskUse(true)
          .exec(
            function(errFindP, documentsP) {
              if (errFindP) {
                // Errors.
                res.status(503);
                return res.json({
                  'success': false,
                  'response': 'err_pagination'
                });
              }
              else {
                return res.json({
                  'success': true,
                  'recordsTotal': totalCount,
                  'recordsFiltered': numFiltered,
                  'response': documentsP
                });
              }
          });
        }
      });
    });
    
    0 讨论(0)
  • 2020-12-03 10:19

    Doing count(1) includes limit and skip.

    0 讨论(0)
  • 2020-12-03 10:21

    By default, count() ignores limit() and counts the results in the entire query. So when you for example do this, var a = db.collection.find(...).limit(10); running a.count() will give you the total count of your query.

    0 讨论(0)
  • 2020-12-03 10:28

    The accepted answer by @johnnycrab is for the mongo CLI.

    If you have to write the same code in Node.js and Express.js, you will have to use it like this to be able to use the "count" function along with the toArray's "result".

    var curFind = db.collection('tasks').find({query});
    

    Then you can run two functions after it like this (one nested in the other)

    curFind.count(function (e, count) {
    
    // Use count here
    
        curFind.skip(0).limit(10).toArray(function(err, result) {
    
        // Use result here and count here
    
        });
    
    });
    
    0 讨论(0)
提交回复
热议问题