UNPKG

@tepez/mongo-cursor-pagination

Version:

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

109 lines (96 loc) 3.58 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 bsonUrlEncoding = require('./utils/bsonUrlEncoding'); /** * Performs a search query on a Mongo collection and pages the results. This is different from * find() in that the results are ordered by their relevancy, and as such, it does not take * a paginatedField parameter. Note that this is less performant than find() because it must * perform the full search on each call to this function. * * @param {MongoCollection} collection A collection object returned from the MongoDB library's. This MUST have a Mongo * $text index on it. * See https://docs.mongodb.com/manual/core/index-text/. * @param {String} searchString String to search on. * @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. {title :1}. * The default is to query ONLY _id (note this is a difference from `find()`). * -next {String} The value to start querying the page. Defaults to start at the beginning of * the results. */ module.exports = (() => { var _ref = _asyncToGenerator(function* (collection, searchString, params) { if (_.isString(params.limit)) params.limit = parseInt(params.limit, 10); if (params.next) params.next = bsonUrlEncoding.decode(params.next); params = _.defaults(params, { query: {}, limit: config.MAX_LIMIT }); if (params.limit < 1) params.limit = 1; if (params.limit > config.MAX_LIMIT) params.limit = config.MAX_LIMIT; // We must perform an aggregate query since Mongo can't query a range when using $text search. const aggregate = [{ $match: _.extend({}, params.query, { $text: { $search: searchString } }) }, { $project: _.extend({}, params.fields, { _id: 1, score: { $meta: 'textScore' } }) }, { $sort: { score: { $meta: 'textScore' }, _id: -1 } }]; if (params.next) { aggregate.push({ $match: { $or: [{ score: { $lt: params.next[0] } }, { score: { $eq: params.next[0] }, _id: { $lt: params.next[1] } }] } }); } aggregate.push({ $limit: params.limit }); let response; const aggregateQuery = collection.aggregate(aggregate); const execMethod = aggregateQuery.toArray ? 'toArray' : 'exec'; const results = yield aggregateQuery[execMethod](); const fullPageOfResults = results.length === params.limit; if (fullPageOfResults) { response = { results, next: bsonUrlEncoding.encode([_.last(results).score, _.last(results)._id]) }; } else { response = { results }; } return response; }); return function (_x, _x2, _x3) { return _ref.apply(this, arguments); }; })();