UNPKG

@tepez/mongo-cursor-pagination

Version:

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

76 lines (65 loc) 3.96 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 config = require('./config'); const { prepareResponse, generateSort, generateCursorQuery } = require('./utils/query'); const sanitizeParams = require('./utils/sanitizeParams'); /** * Performs a find() query on a passed-in Mongo collection, using criteria you specify. The results * are ordered by the paginatedField. * * @param {MongoCollection} collection A collection object returned from the MongoDB library's. * @param {Object} params * -query {Object} The find query. * -limit {Number} The page size. Must be between 1 and `config.MAX_LIMIT`. * -fields {Object} Fields to query in the Mongo object format, e.g. {_id: 1, timestamp :1}. * The default is to query all fields. * -paginatedField {String} The field name to query the range for. The field must be: * 1. Orderable. We must sort by this value. If duplicate values for paginatedField field * exist, the results will be secondarily ordered by the _id. * 2. Indexed. For large collections, this should be indexed for query performance. * 3. Immutable. If the value changes between paged queries, it could appear twice. * The default is to use the Mongo built-in '_id' field, which satisfies the above criteria. * The only reason to NOT use the Mongo _id field is if you chose to implement your own ids. * -next {String} The value to start querying the page. * -previous {String} The value to start querying previous page. * -after {String} The _id to start querying the page. * -before {String} The _id to start querying previous page. * -hint {String} An optional index hint to provide to the mongo query */ module.exports = (() => { var _ref = _asyncToGenerator(function* (collection, params) { const removePaginatedFieldInResponse = params.fields && !params.fields[params.paginatedField]; params = _.defaults((yield sanitizeParams(collection, params)), { query: {} }); const cursorQuery = generateCursorQuery(params); const $sort = generateSort(params); const findOptions = {}; if (params.fields) findOptions.projection = params.fields; const query = collection.find({ $and: [cursorQuery, params.query] }, findOptions); /** * IMPORTANT * * If using a global collation setting, ensure that your collections' indexes (that index upon string fields) * have been created with the same collation option; if this isn't the case, your queries will be unable to * take advantage of any indexes. * * See mongo documentation: https://docs.mongodb.com/manual/reference/collation/#collation-and-index-use */ const collatedQuery = config.COLLATION ? query.collation(config.COLLATION) : query; // Query one more element to see if there's another page. const cursor = collatedQuery.sort($sort).limit(params.limit + 1); if (params.hint) cursor.hint(params.hint); const execMethod = cursor.toArray ? 'toArray' : 'exec'; const results = yield cursor[execMethod](); const response = prepareResponse(results, params); // Remove fields that we added to the query (such as paginatedField and _id) that the user didn't ask for. if (removePaginatedFieldInResponse) { response.results = _.map(response.results, function (result) { return _.omit(result, params.paginatedField); }); } return response; }); return function (_x, _x2) { return _ref.apply(this, arguments); }; })();