プッシュとスライスを使用した解決策があります: https://stackoverflow.com/a/39784851/4752635 (@emaniacsはここでもそれについて言及しています。)
しかし、私は2つのクエリを使用することを好みます。 $$ROOTをプッシュして$sliceを使用するソリューションでは、大規模なコレクションの場合、ドキュメントメモリの制限が16MBになります。また、大規模なコレクションの場合、2つのクエリを一緒にすると、$$ROOTをプッシュするクエリよりも高速に実行されるようです。それらを並行して実行することもできるので、2つのクエリのうち遅い方(おそらくソートするクエリ)によってのみ制限されます。
- 最初にフィルタリングし、次にIDでグループ化して、フィルタリングされた要素の数を取得します。ここではフィルタリングしないでください。不要です。
- フィルタリング、並べ替え、ページ分割を行う2番目のクエリ。
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
});
}
});
}
});
});