MongoDB Aggregation: Как получить общее количество записей?
Я использовал агрегацию для извлечения записей из 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. Поэтому я хотел бы получить общее количество записей. Как я могу сделать с агрегацией? Пожалуйста, посоветуйте мне. Благодаря
Ответы
Ответ 1
Это один из наиболее часто задаваемых вопросов для получения разбитого на результат результата и общего количества результатов одновременно в одном запросе. Я не могу объяснить, как я себя чувствовал, когда я, наконец, достиг этого LOL.
$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": [
{...},
{...},
{...},
]
}
]
Ответ 2
Начиная с v.3.4 (я думаю), у MongoDB появился новый оператор конвейера агрегации с именем ' facet ', который, по их собственным словам:
Обрабатывает несколько конвейеров агрегации на одном этапе в одном наборе входных документов. Каждый субпроводник имеет свое поле в выходном документе, где его результаты хранятся в виде массива документов.
В данном конкретном случае это означает, что можно сделать что-то вроде этого:
$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}]
}
]
Ответ 3
Используйте это, чтобы найти общее количество в коллекции.
db.collection.aggregate( [
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );
Ответ 4
Вы можете использовать функцию toArray, а затем получить ее длину для общего количества записей.
db.CollectionName.aggregate([....]).toArray().length
Ответ 5
Я сделал это следующим образом:
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);
Ответ 6
Используйте этап конвейера агрегации $ count, чтобы получить общее количество документов:
Запрос:
db.collection.aggregate(
[
{
$match: {
...
}
},
{
$group: {
...
}
},
{
$count: "totalCount"
}
]
)
Результат:
{
"totalCount" : Number of records (some integer value)
}
Ответ 7
Решение, предоставляемое @Divergent, работает, но по моему опыту лучше иметь 2 запроса:
- Сначала для фильтрации, а затем группировки по идентификатору, чтобы получить количество фильтрованных элементов. Не фильтруйте здесь, это не нужно.
- Второй запрос, который фильтрует, сортирует и разбивает страницы.
Решение с нажатием $$ ROOT и использованием $slice работает с ограничением памяти документа 16 МБ для больших коллекций. Кроме того, для больших коллекций два запроса вместе работают быстрее, чем один с нажатием $$ 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
});
}
});
}
});
});
Ответ 8
Это может работать для нескольких условий совпадения
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) => {})
Ответ 9
Извините, но я думаю, вам нужны два запроса. Один для общих просмотров и другой для сгруппированных записей.
Вы можете найти полезный этот ответ
Ответ 10
//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});