mongoose-paginate-v2
Version:
A custom pagination library for Mongoose with customizable labels.
282 lines (272 loc) • 10 kB
JavaScript
;
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;