UNPKG

@tepez/mongo-cursor-pagination

Version:

Make it easy to return cursor-paginated results from a Mongo collection

88 lines (75 loc) 3.84 kB
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; })();