MongoDB 聚合: 如何获得总记录计数?

我使用聚合来从 mongodb 获取记录。

$result = $collection->aggregate(array(
array('$match' => $document),
array('$group' => array('_id' => '$book_id', 'date' => array('$max' => '$book_viewed'),  'views' => array('$sum' => 1))),
array('$sort' => $sort),
array('$skip' => $skip),
array('$limit' => $limit),
));

如果我无限制地执行这个查询,那么将获取10条记录。但是我希望保持2的限制。所以我想知道记录总数。我如何处理聚合?请指教。谢谢

209017 次浏览

使用此选项可查找结果集合中的总计数。

db.collection.aggregate( [
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );

对不起,但我认为您需要两个查询。一个查询总视图,另一个查询分组记录。

你可以找到有用的 这个答案

这是在单个查询中同时获得分页结果和结果总数的最常见问题之一。我无法解释当我最终达到这个目标时的感受,哈哈。

$result = $collection->aggregate(array(
array('$match' => $document),
array('$group' => array('_id' => '$book_id', 'date' => array('$max' => '$book_viewed'),  'views' => array('$sum' => 1))),
array('$sort' => $sort),


// get total, AND preserve the results
array('$group' => array('_id' => null, 'total' => array( '$sum' => 1 ), 'results' => array( '$push' => '$$ROOT' ) ),
// apply limit and offset
array('$project' => array( 'total' => 1, 'results' => array( '$slice' => array( '$results', $skip, $length ) ) ) )
))

结果会是这样的:

[
{
"_id": null,
"total": ...,
"results": [
{...},
{...},
{...},
]
}
]

@ Divergent 提供的解决方案确实有效,但根据我的经验,最好有两个查询:

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

您可以使用 toArray 函数,然后获取总记录计数的长度。

db.CollectionName.aggregate([....]).toArray().length

我是这样做的:

db.collection.aggregate([
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] ).map(function(record, index){
print(index);
});

聚合将返回数组,因此只需循环它并获得最终的索引。

另一种方法是:

var count = 0 ;
db.collection.aggregate([
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] ).map(function(record, index){
count++
});
print(count);

自从 v. 3.4(我认为) MongoDB 现在有了一个名为‘ 方面’的新聚合管道运算符,用他们自己的话来说:

在同一组输入文档的单个阶段内处理多个聚合管道。每个子管道在输出文档中都有自己的字段,其结果存储为一个文档数组。

在这种特殊情况下,这意味着人们可以这样做:

$result = $collection->aggregate([
{ ...execute queries, group, sort... },
{ ...execute queries, group, sort... },
{ ...execute queries, group, sort... },
{
$facet: {
paginatedResults: [{ $skip: skipPage }, { $limit: perPage }],
totalCount: [
{
$count: 'count'
}
]
}
}
]);

结果如下(按100个总成绩计算) :

[
{
"paginatedResults":[{...},{...},{...}, ...],
"totalCount":[{"count":100}]
}
]

使用 $count 聚合流水线阶段获取文档总数:

质疑:

db.collection.aggregate(
[
{
$match: {
...
}
},
{
$group: {
...
}
},
{
$count: "totalCount"
}
]
)

结果:

{
"totalCount" : Number of records (some integer value)
}

这可能适用于多个匹配条件

            const query = [
{
$facet: {
cancelled: [
{ $match: { orderStatus: 'Cancelled' } },
{ $count: 'cancelled' }
],
pending: [
{ $match: { orderStatus: 'Pending' } },
{ $count: 'pending' }
],
total: [
{ $match: { isActive: true } },
{ $count: 'total' }
]
}
},
{
$project: {
cancelled: { $arrayElemAt: ['$cancelled.cancelled', 0] },
pending: { $arrayElemAt: ['$pending.pending', 0] },
total: { $arrayElemAt: ['$total.total', 0] }
}
}
]
Order.aggregate(query, (error, findRes) => {})
//const total_count = await User.find(query).countDocuments();
//const users = await User.find(query).skip(+offset).limit(+limit).sort({[sort]: order}).select('-password');
const result = await User.aggregate([
{$match : query},
{$sort: {[sort]:order}},
{$project: {password: 0, avatarData: 0, tokens: 0}},
{$facet:{
users: [{ $skip: +offset }, { $limit: +limit}],
totalCount: [
{
$count: 'count'
}
]
}}
]);
console.log(JSON.stringify(result));
console.log(result[0]);
return res.status(200).json({users: result[0].users, total_count: result[0].totalCount[0].count});

我需要应用聚合后的绝对总计数,这对我很有用:

db.mycollection.aggregate([
{
$group: {
_id: { field1: "$field1", field2: "$field2" },
}
},
{
$group: {
_id: null, count: { $sum: 1 }
}
}
])

结果:

{
"_id" : null,
"count" : 57.0
}

如果你不想分组,那么使用以下方法:

聚合([ { $match: { score: { $gt: 70,$lte: 90}}} , { $count: ‘ count’} ]) ;

以下是在进行 MongoDB 聚合时获得总记录计数的一些方法:


  • 使用 $count:

    db.collection.aggregate([
    // Other stages here
    { $count: "Total" }
    ])
    

    对于获得1000记录,这需要平均2毫秒,是最快的方式。


  • 使用 .toArray():

    db.collection.aggregate([...]).toArray().length
    

    要获得1000条记录,平均需要18毫秒。


  • 使用 .itcount():

    db.collection.aggregate([...]).itcount()
    

    要获得1000条记录,平均需要14毫秒。

如果您需要 $match 与嵌套文档,然后

Https://mongoplayground.net/p/dpx6cfhr_mm

db.collection.aggregate([
{
"$unwind": "$tags"
},
{
"$match": {
"$or": [
{
"tags.name": "Canada"
},
{
"tags.name": "ABC"
}
]
}
},
{
"$group": {
"_id": null,
"count": {
"$sum": 1
}
}
}
])

我不得不进行查找,匹配,然后数收到的文件。我是这样用猫鼬做到的:

ModelName.aggregate([
{
'$lookup': {
'from': 'categories',
'localField': 'category',
'foreignField': '_id',
'as': 'category'
}
}, {
'$unwind': {
'path': '$category'
}
}, {
'$match': {
'category.price': {
'$lte': 3,
'$gte': 0
}
}
}, {
'$count': 'count'
}
]);

下面是一个分页、匹配和猫鼬聚合排序的示例

const [response] = await Prescribers.aggregate([
{ $match: searchObj },
{ $sort: sortObj },
{
$facet: {
response: [{ $skip: count * page }, { $limit: count }],
pagination: [
{
$count: 'totalDocs',
},
{
$addFields: {
page: page + 1,
totalPages: {
$floor: {
$divide: ['$totalDocs', count],
},
},
},
},
],
},
},
]);

这里的计数是每个页面的限制,页面是页码。开处方是模型

这将返回与此类似的记录

"data": {
"response": [
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
}
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
},
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
}
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
},
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
},
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
}
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
},
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
}
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
},
{
"_id": "6349308c90e58c6820bbc682",
"foo": "bar"
},
],
"pagination": [
{
"totalDocs": 592438,
"page": 1,
"totalPages": 59243
}
]
}