出于速度考虑,我想将查询结果限制为10条
db.collection.find( ... ).limit(10)
不过,我也想知道总数,比如说“有124个,但我只有10个”。是否有一个好的有效方法来做到这一点?
默认情况下,count()
忽略 limit()
并计算整个查询结果的数量。
因此,如果你做了这样的查询:var a = db.collection.find(...).limit(10);
运行 a.count()
将会给出您查询的总数。
使用count(1)会包括limit和skip。
TypeError: with_limit_and_skip must be True or False
,
使用count(True)
即可解决。 - kkzxak47@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
});
});
cursor.count()
默认情况下应该忽略cursor.skip()
和cursor.limit()
。
来源:http://docs.mongodb.org/manual/reference/method/cursor.count/#cursor.count
您可以使用$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
。
有一个使用push和slice的解决方案:https://dev59.com/vGIj5IYBdhLWcg3wWj-d#39784851
我更喜欢
使用推送$$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
});
}
});
}
});
});