@tepez/mongo-cursor-pagination
Version:
Make it easy to return cursor-paginated results from a Mongo collection
88 lines (75 loc) • 3.84 kB
JavaScript
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
const _ = require('underscore');
const bsonUrlEncoding = require('./bsonUrlEncoding');
const getPropertyViaDotNotation = require('./getPropertyViaDotNotation');
const config = require('../config');
module.exports = (() => {
var _ref = _asyncToGenerator(function* (collection, params) {
if (params.previous) params.previous = bsonUrlEncoding.decode(params.previous);
if (params.next) params.next = bsonUrlEncoding.decode(params.next);
params = _.defaults(params, {
limit: config.DEFAULT_LIMIT,
paginatedField: '_id'
});
if (params.limit < 1) params.limit = 1;
if (params.limit > config.MAX_LIMIT) params.limit = config.MAX_LIMIT;
// If the paginated field is not _id, then it might have duplicate values in it. This is bad
// because then we can't exclusively use it for our range queries (that use $lt and $gt). So
// to fix this, we secondarily sort on _id, which is always unique.
const shouldSecondarySortOnId = params.paginatedField !== '_id';
//
// params.after - overides params.next
//
// The 'after' param sets the start position for the next page. This is similar to the
// 'next' param, with the difference that 'after' takes a plain _id instead of an encoded
// string of both _id and paginatedField values.
if (params.after) {
if (shouldSecondarySortOnId) {
// Since the primary sort field is not provided by the 'after' pagination cursor we
// have to look it up when the paginated field is not _id.
const doc = yield collection.findOne({ _id: params.after }, { [params.paginatedField]: true, _id: false });
if (doc) {
// Handle usage of dot notation in paginatedField
const prop = getPropertyViaDotNotation(params.paginatedField, doc);
params.next = [prop, params.after];
}
} else {
params.next = params.after;
}
}
//
// params.before - overides params.previous
//
// The 'before' param sets the start position for the previous page. This is similar to the
// 'previous' param, with the difference that 'before' takes a plain _id instead of an encoded
// string of both _id and paginatedField values.
if (params.before) {
if (shouldSecondarySortOnId) {
// Since the primary sort field is not provided by the 'before' pagination cursor we
// have to look it up when the paginated field is not _id.
const doc = yield collection.findOne({ _id: params.before }, { [params.paginatedField]: true, _id: false });
if (doc) {
// Handle usage of dot notation in paginatedField
const prop = getPropertyViaDotNotation(params.paginatedField, doc);
params.previous = [prop, params.before];
}
} else {
params.previous = params.before;
}
}
// The query must always include the paginatedField so we can construct the cursor.
if (params.fields) {
params.fields = _.extend({
_id: 0 // Mongo includes this field by default, so don't request it unless the user wants it.
}, params.fields);
if (!params.fields[params.paginatedField]) {
params.fields[params.paginatedField] = 1;
}
}
return params;
});
function sanitizeParams(_x, _x2) {
return _ref.apply(this, arguments);
}
return sanitizeParams;
})();