mongoose-paginate-v2
Version:
A custom pagination library for Mongoose with customizable labels.
351 lines (337 loc) • 14 kB
JavaScript
;
var _excluded = ["limit", "skip"];
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* @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(query: Object=): Promise<number>)} [options.useCustomCountFn=false] - Use custom function for count datasets. Receives `query` as an optional argument.
* @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 hasOwn(obj, key) {
return Object.prototype.hasOwnProperty.call(obj || {}, key);
}
function isSessionInTransaction(session) {
if (session == null) {
return false;
}
if (typeof session.inTransaction === 'function') {
return session.inTransaction();
}
// Older driver sessions may expose the driver session via `session.session`.
var driverSession = session.session;
if (driverSession && typeof driverSession.inTransaction === 'function') {
return driverSession.inTransaction();
}
// Pre-inTransaction drivers: conservatively serialize because we cannot
// determine whether the session is transactional. These drivers are too old
// to support causal sessions reliably anyway.
return true;
}
function hasActiveSession(model, queryOptions) {
if (hasOwn(queryOptions, 'session')) {
return isSessionInTransaction(queryOptions.session);
}
var base = model && model.db && model.db.base;
var als = base && base.transactionAsyncLocalStorage;
if (!als || typeof als.getStore !== 'function') {
return false;
}
var store = als.getStore();
return isSessionInTransaction(store && store.session);
}
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;
// Create queryOptions with collation included to preserve session context in transactions
var queryOptions = Object.keys(collation).length > 0 ? _objectSpread(_objectSpread({}, findOptions), {}, {
collation
}) : findOptions;
// For countDocuments, exclude limit/skip which MongoDB applies to the count result
// eslint-disable-next-line no-unused-vars
var _ref = queryOptions || {},
_l = _ref.limit,
_s = _ref.skip,
countOptions = _objectWithoutProperties(_ref, _excluded);
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
// Используем estimatedDocumentCount, если query пустой, иначе countDocuments
if (!query || Object.keys(query).length === 0) {
countPromise = this.estimatedDocumentCount().exec();
} else {
countPromise = this.countDocuments(query, countOptions).exec();
}
} else {
if (useEstimatedCount === true) {
countPromise = this.estimatedDocumentCount().exec();
} else if (typeof useCustomCountFn === 'function') {
countPromise = useCustomCountFn(query);
} else {
// Используем estimatedDocumentCount, если query пустой, иначе countDocuments
if (!query || Object.keys(query).length === 0) {
countPromise = this.estimatedDocumentCount().exec();
} else {
countPromise = this.countDocuments(query, countOptions).exec();
}
}
}
}
var model = this;
function buildDocsPromise() {
if (!limit) {
return Promise.resolve([]);
}
// Use queryOptions (which includes collation) to preserve session context in transactions
var mQuery = model[customFind](query, projection, queryOptions);
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);
}
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`.');
}
var execPromise = mQuery.exec();
if (lean && leanWithId) {
execPromise = execPromise.then(function (docs) {
docs.forEach(function (doc) {
if (doc._id) {
doc.id = String(doc._id);
}
});
return docs;
});
}
return execPromise;
}
function buildResult(count, docs) {
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
if (labelLimit) meta[labelLimit] = count;
if (labelTotalPages) meta[labelTotalPages] = 1;
if (labelPage) meta[labelPage] = page;
if (labelPagingCounter) meta[labelPagingCounter] = (page - 1) * limit + 1;
if (labelHasPrevPage) meta[labelHasPrevPage] = false;
if (labelHasNextPage) meta[labelHasNextPage] = false;
if (labelPrevPage) meta[labelPrevPage] = null;
if (labelNextPage) meta[labelNextPage] = null;
if (pagination) {
if (labelLimit) meta[labelLimit] = limit;
if (labelTotalPages) meta[labelTotalPages] = pages;
// Set prev page
if (page > 1) {
if (labelHasPrevPage) meta[labelHasPrevPage] = true;
if (labelPrevPage) meta[labelPrevPage] = page - 1;
} else if (page == 1 && typeof offset !== 'undefined' && offset !== 0) {
if (labelHasPrevPage) meta[labelHasPrevPage] = true;
if (labelPrevPage) meta[labelPrevPage] = 1;
}
// Set next page
if (page < pages) {
if (labelHasNextPage) meta[labelHasNextPage] = true;
if (labelNextPage) meta[labelNextPage] = page + 1;
}
}
// Remove customLabels set to false
delete meta['false'];
if (limit == 0) {
if (labelLimit) meta[labelLimit] = 0;
if (labelTotalPages) meta[labelTotalPages] = 1;
if (labelPage) meta[labelPage] = 1;
if (labelPagingCounter) meta[labelPagingCounter] = 1;
if (labelPrevPage) meta[labelPrevPage] = null;
if (labelNextPage) meta[labelNextPage] = null;
if (labelHasPrevPage) meta[labelHasPrevPage] = false;
if (labelHasNextPage) meta[labelHasNextPage] = false;
}
if (labelMeta) {
result = {
[labelDocs]: docs,
[labelMeta]: meta
};
} else {
result = _objectSpread({
[labelDocs]: docs
}, meta);
}
return result;
}
var hasSession = hasActiveSession(model, queryOptions);
var resultPromise;
if (hasSession) {
resultPromise = Promise.resolve(countPromise).then(function (count) {
return buildDocsPromise().then(function (docs) {
return buildResult(count, docs);
});
});
} else {
docsPromise = buildDocsPromise();
resultPromise = Promise.all([countPromise, docsPromise]).then(function (values) {
return buildResult(values[0], values[1]);
});
}
return resultPromise.then(function (result) {
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;