UNPKG

mongoose-paginator-advanced

Version:

Pagination plugin for ORM Mongoose based on mongoose-paginator-simple

195 lines (166 loc) 7.5 kB
'use strict'; let util = require('util'); let mongoose = require('mongoose'); module.exports = function (schema, schemaOptions) { schema.static('paginate', function (criteria, options, callback) { let self = this; callback = options && typeof options === 'function' ? options : callback; let o = buildOptions(options, schemaOptions); o.sort = o.convertSort(o.sort, schema); criteria = o.criteriaWrapper(o.convertCriteria(criteria, schema)); return new Promise(function (resolve, reject) { let resultFn = (err, count, docs) => { let result = {total: count, limit: o.limit || count, page: o.page, data: docs}; if (err) { callback && callback(err); return reject(err); } callback && callback(null, result); return resolve(result); }; self.where(criteria).count().exec().then(function (count) { if (count <= 0) { return resultFn(null, count, {}); } let query = self.find(criteria).select(o.select).populate(o.populate).lean(o.lean).skip(o.skip); if (o.sort) { query.sort(o.sort); } if (o.limit && o.limit > 0) { query.limit(o.limit); } query.exec().then(function (docs) { return resultFn(null, count, docs); }); }, function (err) { resultFn(err); }); }); }); schema.static('aggregatePaginated', function (pipeline, options, callback) { let self = this; callback = options && typeof options === 'function' ? options : callback; let o = buildOptions(options, schemaOptions); if (!util.isArray(pipeline)) { pipeline = [pipeline] } return new Promise((resolve, reject) => { let admin = new mongoose.mongo.Admin(mongoose.connection.db); admin.buildInfo(function (err, info) { if(/^3\.4/.exec(info.version) === null) { return reject('Unsupported MongoDb version'); } let stages = {}, countMode = false, rPipeLine = []; for (let stage of pipeline) { let s = stage; let keys = Object.keys(stage); if(keys.length > 1) { reject('Invalid pipeline specified'); } // store special stages if (keys[0] === '$sort') { stages['$sort'] = s; } else if (keys[0] === '$skip') { stages['$skip'] = s; } else if (keys[0] === '$limit') { stages['$limit'] = s; } else { if(keys[0] === '$count') { countMode = true; } if(keys[0] === '$match') { s = { $match: o.criteriaWrapper(o.convertCriteria(stage.$match, schema)) } } rPipeLine.push(s); } } let resultWrapper = (err, count, resultData) => { let result = {total: count, limit: o.limit || count, page: o.page, data: resultData}; if (err) { callback && callback(err); return reject(err); } callback && callback(null, result); return resolve(result); }; if(!countMode) { // build count-pipeline for counting result length let countPipeLine = rPipeLine.slice(0); countPipeLine.push({ $count: 'total' }); self.aggregate(countPipeLine).then((data) => { return data; }).then((countData) => { if(!countData.length) { resultWrapper(null, 0, countData); } else { // add sort, limit and skip to main pipeline if(stages.hasOwnProperty('$sort')) { rPipeLine.push({ $sort: o.convertSort(stages['$sort'].$sort) }); } else if(o.sort) { rPipeLine.push({ $sort: o.convertSort(o.sort) }) } if(stages.hasOwnProperty('$skip')) { rPipeLine.push(stages['$skip']); } else { rPipeLine.push({ $skip: o.skip }); } if(stages.hasOwnProperty('$limit')) { rPipeLine.push(stages['$limit']); } else if(o.limit) { rPipeLine.push({ $limit: o.limit }); } // perform main pipeline self.aggregate(rPipeLine).then((data) => { resultWrapper(null, countData[0].total, data); }) } }).catch((err) => { reject(err); }); } else { // pipeline is made for count records self.aggregate(rPipeLine).then((data) => { resolve(data); }).catch((err) => { reject(err); }); } }); }); }); }; function buildOptions(options, schemaOptions) { let o = Object.assign({}, options || {}, schemaOptions || {}); // Basic options o.limit = o.limit && typeof o.limit === 'function' ? o.limit(o.maxLimit) : ((o.limit && o.maxLimit && o.limit > o.maxLimit) || !o.limit ? o.maxLimit : o.limit); o.page = o.page || 1; o.skip = (o.page - 1) * (o.limit || 0); o.lean = o.lean !== undefined ? o.lean : true; // Plain object, more peformance o.select = o.select && typeof o.select === 'function' ? o.select() : o.select; o.populate = o.populate && typeof o.populate === 'function' ? o.populate() : (o.populate || ''); // Advanced options let convert = (converter) => { return converter && typeof converter === 'function' ? converter : function (value, schema) { return value; }; }; o.convertSort = convert(o.convertSort); o.convertCriteria = convert(o.convertCriteria); o.criteriaWrapper = o.criteriaWrapper && typeof o.criteriaWrapper === 'function' ? o.criteriaWrapper : function (criteria) { return criteria; }; return o; }