Last active
April 23, 2024 17:48
-
-
Save sdgluck/ce8f9b8b9d64c9e25a1e71d53a5ba128 to your computer and use it in GitHub Desktop.
Pagination using Mongo: populate, match, sort, count, and limit with the aggregation pipeline
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Query blog posts by user -> paginated results and a total count. | |
* @param userId {ObjectId} ID of user to retrieve blog posts for | |
* @param startRow {Number} First row to return in results | |
* @param endRow {Number} Last row to return in results | |
* @param [filter] {Object} Optional extra matching query object | |
* @param [sort] {Object} Optional sort query object | |
* @returns {Object} Object -> `{ rows, count }` | |
*/ | |
function queryBlogPostsByUser (userId, startRow, endRow, filter = {}, sort = false) { | |
if (!(user instanceof mongoose.Types.ObjectId)) { | |
throw new Error('userId must be ObjectId') | |
} else if (typeof startRow !== 'number') { | |
throw new Error('startRow must be number') | |
} else if (typeof endRow !== 'number') { | |
throw new Error('endRow must be number') | |
} | |
const query = [ | |
// more lookups go here if you need them | |
// we have a many-to-one from blogPost -> user | |
{ $lookup: { | |
from: 'users', | |
localField: 'user', | |
foreignField: '_id', | |
as: 'user' | |
} }, | |
// each blog has a single user (author) so flatten it using $unwind | |
{ $unwind: '$user' }, | |
// filter the results by our userId | |
{ $match: Object.assign({ 'user._id': userId }, filter) } | |
] | |
if (sort) { | |
// maybe we want to sort by blog title or something | |
query.push({ $sort: sort }) | |
} | |
query.push( | |
{ $group: { | |
_id: null, | |
// get a count of every result that matches until now | |
count: { $sum: 1 }, | |
// keep our results for the next operation | |
results: { $push: '$$ROOT' } | |
} }, | |
// and finally trim the results to within the range given by start/endRow | |
{ $project: { | |
count: 1, | |
rows: { $slice: ['$results', startRow, endRow] } | |
} } | |
) | |
return BlogPost | |
.aggregate(query) | |
.then(([{ count, rows }]) => ({ count, rows })) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment