UNPKG

mongoose-paginate-v2

Version:

A custom pagination library for Mongoose with customizable labels.

282 lines (272 loc) 10 kB
"use strict"; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * @param {Object} [query={}] * @param {Object} [options={}] * @param {Object|String} [options.select=''] * @param {Object|String} [options.projection={}] * @param {Object} [options.options={}] * @param {Object|String} [options.sort] * @param {Object|String} [options.customLabels] * @param {Object} [options.collation] * @param {Array|Object|String} [options.populate] * @param {Boolean} [options.lean=false] * @param {Boolean} [options.leanWithId=true] * @param {Boolean} [options.leanWithVirtuals=false] * @param {Number} [options.offset=0] - Use offset or page to set skip position * @param {Number} [options.page=1] * @param {Number} [options.limit=10] * @param {Boolean} [options.useEstimatedCount=true] - Enable estimatedDocumentCount for larger datasets. As the name says, the count may not abe accurate. * @param {Function} [options.useCustomCountFn=false] - use custom function for count datasets. * @param {Object} [options.read={}] - Determines the MongoDB nodes from which to read. * @param {Function} [callback] * * @returns {Promise} */ var PaginationParametersHelper = require('./pagination-parameters'); var paginateSubDocsHelper = require('./pagination-subdocs'); var paginateQueryHelper = require('./pagination-queryHelper'); var defaultOptions = { customLabels: { totalDocs: 'totalDocs', limit: 'limit', page: 'page', totalPages: 'totalPages', docs: 'docs', nextPage: 'nextPage', prevPage: 'prevPage', pagingCounter: 'pagingCounter', hasPrevPage: 'hasPrevPage', hasNextPage: 'hasNextPage', meta: null }, collation: {}, lean: false, leanWithId: true, leanWithVirtuals: false, limit: 10, projection: {}, select: '', options: {}, pagination: true, useEstimatedCount: false, useCustomCountFn: false, forceCountFn: false, allowDiskUse: false, customFind: 'find' }; function paginate(query, options, callback) { options = _objectSpread(_objectSpread(_objectSpread({}, defaultOptions), paginate.options), options); query = query || {}; var _options = options, collation = _options.collation, lean = _options.lean, leanWithId = _options.leanWithId, leanWithVirtuals = _options.leanWithVirtuals, populate = _options.populate, projection = _options.projection, read = _options.read, select = _options.select, sort = _options.sort, pagination = _options.pagination, useEstimatedCount = _options.useEstimatedCount, useCustomCountFn = _options.useCustomCountFn, forceCountFn = _options.forceCountFn, allowDiskUse = _options.allowDiskUse, customFind = _options.customFind; var customLabels = _objectSpread(_objectSpread({}, defaultOptions.customLabels), options.customLabels); var limit = defaultOptions.limit; if (pagination && !isNaN(Number(options.limit))) { limit = parseInt(options.limit, 10) > 0 ? parseInt(options.limit, 10) : 0; } var isCallbackSpecified = typeof callback === 'function'; var findOptions = options.options; var offset; var page; var skip; var docsPromise = []; // Labels var labelDocs = customLabels.docs; var labelLimit = customLabels.limit; var labelNextPage = customLabels.nextPage; var labelPage = customLabels.page; var labelPagingCounter = customLabels.pagingCounter; var labelPrevPage = customLabels.prevPage; var labelTotal = customLabels.totalDocs; var labelTotalPages = customLabels.totalPages; var labelHasPrevPage = customLabels.hasPrevPage; var labelHasNextPage = customLabels.hasNextPage; var labelMeta = customLabels.meta; if (Object.prototype.hasOwnProperty.call(options, 'offset')) { offset = parseInt(options.offset, 10); skip = offset; } else if (Object.prototype.hasOwnProperty.call(options, 'page')) { page = parseInt(options.page, 10) > 0 ? parseInt(options.page, 10) : 1; skip = (page - 1) * limit; } else { offset = 0; page = 1; skip = offset; } if (!pagination) { page = 1; } var countPromise; // Only run count when pagination is enabled if (pagination) { if (forceCountFn === true) { // Deprecated since starting from MongoDB Node.JS driver v3.1 // Hack for mongo < v3.4 if (Object.keys(collation).length > 0) { countPromise = this.countDocuments(query, findOptions).collation(collation).exec(); } else { countPromise = this.countDocuments(query).exec(); } } else { if (useEstimatedCount === true) { countPromise = this.estimatedDocumentCount().exec(); } else if (typeof useCustomCountFn === 'function') { countPromise = useCustomCountFn(); } else { // Hack for mongo < v3.4 if (Object.keys(collation).length > 0) { countPromise = this.countDocuments(query, findOptions).collation(collation).exec(); } else { countPromise = this.countDocuments(query).exec(); } } } } if (limit) { var mQuery = this[customFind](query, projection, findOptions); if (populate) { mQuery.populate(populate); } mQuery.select(select); mQuery.sort(sort); if (lean) { // use whit mongoose-lean-virtuals if (leanWithVirtuals) { mQuery.lean({ virtuals: leanWithVirtuals }); } else { mQuery.lean(lean); } } if (read && read.pref) { /** * Determines the MongoDB nodes from which to read. * @param read.pref one of the listed preference options or aliases * @param read.tags optional tags for this query */ mQuery.read(read.pref, read.tags); } // Hack for mongo < v3.4 if (Object.keys(collation).length > 0) { mQuery.collation(collation); } if (pagination) { mQuery.skip(skip); mQuery.limit(limit); } try { if (allowDiskUse === true) { mQuery.allowDiskUse(); } } catch (ex) { console.error('Your MongoDB version does not support `allowDiskUse`.'); } docsPromise = mQuery.exec(); if (lean && leanWithId) { docsPromise = docsPromise.then(function (docs) { docs.forEach(function (doc) { if (doc._id) { doc.id = String(doc._id); } }); return docs; }); } } return Promise.all([countPromise, docsPromise]).then(function (values) { var count = values[0]; var docs = values[1]; if (pagination !== true) { count = docs.length; } var meta = { [labelTotal]: count }; var result = {}; if (typeof offset !== 'undefined') { meta.offset = offset; page = Math.ceil((offset + 1) / limit); } var pages = limit > 0 ? Math.ceil(count / limit) || 1 : null; // Setting default values meta[labelLimit] = count; meta[labelTotalPages] = 1; meta[labelPage] = page; meta[labelPagingCounter] = (page - 1) * limit + 1; meta[labelHasPrevPage] = false; meta[labelHasNextPage] = false; meta[labelPrevPage] = null; meta[labelNextPage] = null; if (pagination) { meta[labelLimit] = limit; meta[labelTotalPages] = pages; // Set prev page if (page > 1) { meta[labelHasPrevPage] = true; meta[labelPrevPage] = page - 1; } else if (page == 1 && typeof offset !== 'undefined' && offset !== 0) { meta[labelHasPrevPage] = true; meta[labelPrevPage] = 1; } // Set next page if (page < pages) { meta[labelHasNextPage] = true; meta[labelNextPage] = page + 1; } } // Remove customLabels set to false delete meta['false']; if (limit == 0) { meta[labelLimit] = 0; meta[labelTotalPages] = 1; meta[labelPage] = 1; meta[labelPagingCounter] = 1; meta[labelPrevPage] = null; meta[labelNextPage] = null; meta[labelHasPrevPage] = false; meta[labelHasNextPage] = false; } if (labelMeta) { result = { [labelDocs]: docs, [labelMeta]: meta }; } else { result = _objectSpread({ [labelDocs]: docs }, meta); } return isCallbackSpecified ? callback(null, result) : Promise.resolve(result); }).catch(function (error) { return isCallbackSpecified ? callback(error) : Promise.reject(error); }); } /** * @param {Schema} schema */ module.exports = function (schema) { schema.statics.paginate = paginate; schema.statics.paginateSubDocs = paginateSubDocsHelper; schema.query.paginate = paginateQueryHelper; }; module.exports.PaginationParameters = PaginationParametersHelper; module.exports.paginateSubDocs = paginateSubDocsHelper; module.exports.paginate = paginate;