在MongoDB中限制结果但仍然获取完整的计数?

45

出于速度考虑,我想将查询结果限制为10条

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

不过,我也想知道总数,比如说“有124个,但我只有10个”。是否有一个好的有效方法来做到这一点?

6个回答

47

默认情况下,count() 忽略 limit() 并计算整个查询结果的数量。 因此,如果你做了这样的查询:var a = db.collection.find(...).limit(10); 运行 a.count() 将会给出您查询的总数。


1
你好,我怎样才能确保限制器已经被应用而不使用count()函数?还有其他的方法吗? - rodrigorf
有没有想法在MongoDB Node.js驱动程序中实现这一点而不需要查询两次? - Akshay Naik

27

使用count(1)会包括limit和skip。


3
根据mongoengine,TypeError: with_limit_and_skip must be True or False, 使用count(True)即可解决。 - kkzxak47
1
.count(with_limit_and_skip=True) 将更易读 - Nameless

6

@johnnycrab提供的答案适用于mongo CLI。

如果您需要在Node.js和Express.js中编写相同的代码,您将需要像这样使用它才能使用"count"函数以及toArray的"result"。

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

然后,您可以像这样运行两个函数(一个嵌套在另一个中):
curFind.count(function (e, count) {

// Use count here

    curFind.skip(0).limit(10).toArray(function(err, result) {

    // Use result here and count here

    });

});

4

0

您可以使用$facet阶段,在同一组输入文档上处理多个聚合管道的单个阶段:

// { item: "a" }
// { item: "b" }
// { item: "c" }
db.collection.aggregate([
  { $facet: {
    limit: [{ $limit: 2 }],
    total: [{ $count: "count" }]
  }},
  { $set: { total: { $first: "$total.count" } } }
])
// { limit: [{ item: "a" }, { item: "b" }], total: 3 }

通过同一查询,您可以同时获取一些文档(limit: [{ $limit: 2 }])和文档的总计数({ $count: "count" })。

最后的$set阶段是一个可选的清理步骤,只需投影$count阶段的结果,使得"total" : [ { "count" : 3 } ]变为total: 3


-1

有一个使用push和slice的解决方案:https://dev59.com/vGIj5IYBdhLWcg3wWj-d#39784851

我更喜欢

  1. 首先按ID分组过滤以获取筛选元素的数量。不要在此处进行过滤,这是不必要的。
  2. 第二个查询进行过滤、排序和分页。

使用推送$$ROOT并使用$slice的解决方案会导致对于大型集合的文档内存限制达到16MB。此外,对于大型集合,两个查询一起似乎比使用$$ROOT推送的查询运行得更快。您也可以并行运行它们,因此您仅受两个查询中较慢的查询(可能是进行排序的查询)的限制。

我已经采用了这个使用2个查询和聚合框架的解决方案(注意-我在这个例子中使用node.js,但思路是相同的):

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
            });
          }
      });
    }
  });
});

2
请不要在多个问题上添加相同的答案。回答最好的一个,并标记其他的为重复。参见是否可以将重复的答案添加到多个问题中? - FelixSFD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接