├── .vscode ├── settings.json └── launch.json ├── .npmignore ├── index.js ├── .travis.yml ├── .gitattributes ├── tsd.json ├── .gitignore ├── LICENSE ├── package.json ├── lib └── mongoose-aggregate-paginate.js ├── README.md └── test └── all.tests.js /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | tsd.json 2 | .gitignore 3 | .gitattributes 4 | node_modules/ 5 | typings/ 6 | .travis.yml 7 | /coverage/ 8 | .vscode 9 | .nyc_output/ 10 | test/ -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const aggregatePaginate = require('./lib/mongoose-aggregate-paginate') 2 | 3 | module.exports = function (schema) { 4 | schema.statics.aggregatePaginate = aggregatePaginate 5 | } 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "15.0" 5 | - "14.0" 6 | - "12.0" 7 | - "11.0" 8 | - "10.0" 9 | services: 10 | - mongodb 11 | after_script: "cat ./coverage/lcov.info | coveralls" 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "mocha/mocha.d.ts": { 9 | "commit": "67605e10f6eb01c9d4a0d13719d804d6dea9e1cb" 10 | }, 11 | "node/node.d.ts": { 12 | "commit": "67605e10f6eb01c9d4a0d13719d804d6dea9e1cb" 13 | }, 14 | "should/should.d.ts": { 15 | "commit": "67605e10f6eb01c9d4a0d13719d804d6dea9e1cb" 16 | }, 17 | "mongoose/mongoose.d.ts": { 18 | "commit": "67605e10f6eb01c9d4a0d13719d804d6dea9e1cb" 19 | }, 20 | "async/async.d.ts": { 21 | "commit": "67605e10f6eb01c9d4a0d13719d804d6dea9e1cb" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Mocha Tests", 11 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 12 | "args": [ 13 | "-u", 14 | "tdd", 15 | "--timeout", 16 | "999999", 17 | "--colors", 18 | "${workspaceFolder}/test" 19 | ], 20 | "internalConsoleOptions": "openOnSessionStart" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | # project specific 46 | /node_modules/ 47 | /typings/ 48 | /coverage/ 49 | .nyc_output/ 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 Maheshkumar Kakade 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongoose-aggregate-paginate", 3 | "version": "2.0.2", 4 | "description": "Mongoose plugin to add pagination for aggregations", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "standard && nyc node_modules/mocha/bin/_mocha --report=lcovonly -- --reporter spec --check-leaks test/", 8 | "cov": "nyc node_modules/mocha/bin/_mocha --report=lcovonly -- --reporter spec --check-leaks test/", 9 | "docker-mongodb": "docker run --rm -it -p 27017:27017 mongo:4.2.8 " 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/Maheshkumar-Kakade/mongoose-aggregate-paginate.git" 14 | }, 15 | "bugs": { 16 | "email": "maheshkumarkakade@gmail.com", 17 | "url": "https://github.com/Maheshkumar-Kakade/mongoose-aggregate-paginate/issues" 18 | }, 19 | "homepage": "https://github.com/Maheshkumar-Kakade/mongoose-aggregate-paginate", 20 | "licenses": [ 21 | { 22 | "type": "MIT", 23 | "url": "https://github.com/Maheshkumar-Kakade/mongoose-aggregate-paginate/LICENSE" 24 | } 25 | ], 26 | "keywords": [ 27 | "mongoose", 28 | "paginate", 29 | "aggregate", 30 | "pagination" 31 | ], 32 | "author": "Maheshkumar Kakade ", 33 | "license": "MIT", 34 | "devDependencies": { 35 | "coveralls": "^3.1.0", 36 | "nyc": "^15.1.0", 37 | "mocha": "^8.4.0", 38 | "mongoose": "^5.12.13", 39 | "should": "^13.2.3", 40 | "standard": "^16.0.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/mongoose-aggregate-paginate.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Paginate Mongoose aggregate result 5 | * @param {Aggregate} aggregate 6 | * @param {any} options {page: number/string default 10, limit: number/string default 10,sort: any default null} 7 | * @param {function} [callback] 8 | * @returns {Promise} 9 | */ 10 | function aggregatePaginate (aggregate, options, callback) { 11 | options = options || {} 12 | const pageNumber = parseInt(options.page || 1, 10) 13 | const resultsPerPage = parseInt(options.limit || 10, 10) 14 | const skipDocuments = (pageNumber - 1) * resultsPerPage 15 | const sort = options.sort 16 | 17 | const q = this.aggregate(aggregate._pipeline) 18 | const countQuery = this.aggregate(q._pipeline) 19 | if (Object.prototype.hasOwnProperty.call(q, 'options')) { 20 | q.options = aggregate.options 21 | countQuery.options = aggregate.options 22 | } 23 | 24 | if (sort) { 25 | q.sort(sort) 26 | } 27 | 28 | return Promise.all([ 29 | q.skip(skipDocuments).limit(resultsPerPage).exec(), 30 | countQuery.group({ 31 | _id: null, 32 | count: { $sum: 1 } 33 | }).exec() 34 | ]) 35 | .then(function (values) { 36 | const count = values[1][0] ? values[1][0].count : 0 37 | if (typeof callback === 'function') { 38 | return callback(null, values[0], Math.ceil(count / resultsPerPage) || 1, values[1][0] ? count : 0) 39 | } 40 | return Promise.resolve({ 41 | docs: values[0], 42 | total: count, 43 | limit: resultsPerPage, 44 | page: pageNumber, 45 | pages: (Math.ceil(count / resultsPerPage) || 1) 46 | }) 47 | }) 48 | .catch(function (reject) { 49 | if (typeof callback === 'function') { 50 | return callback(reject) 51 | } 52 | return Promise.reject(reject) 53 | }) 54 | } 55 | 56 | module.exports = aggregatePaginate 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mongoose-aggregate-paginate 2 | 3 | > `mongoose-aggregate-paginate` is a [Mongoose][mongoose] plugin easy to add pagination for aggregates. This plugin can be used in combination with view pagination middleware such as [express-paginate](https://github.com/niftylettuce/express-paginate). 4 | 5 | [![Build Status][travis-ci-img]][travis-ci-url] 6 | [![npm version][npm-version-img]][npm-version-url] 7 | [![Dependency Status][dependency-status-img]][dependency-status-url] 8 | [![Test Coverage][coveralls-image]][coveralls-url] 9 | [![js-standard-style][js-standard-style-img]][js-standard-style-url] 10 | 11 | 12 | [![NPM](https://nodei.co/npm/mongoose-aggregate-paginate.png?downloadRank=true&downloads=true)](https://nodei.co/npm/mongoose-aggregate-paginate/) 13 | 14 | ## Index 15 | * [Install](#install) 16 | * [Usage](#usage) 17 | * [License](#license) 18 | 19 | ## Install 20 | 21 | ```bash 22 | npm install mongoose-aggregate-paginate --save 23 | ``` 24 | ## Usage 25 | 26 | This plugin must first be added to a schema: 27 | 28 | ```js 29 | 30 | let mongooseAggregatePaginate = require('mongoose-aggregate-paginate'); 31 | 32 | mySchema.plugin(mongooseAggregatePaginate); 33 | 34 | ``` 35 | 36 | `MyModel` will have a new function called `paginate` (e.g. `MyModel.aggregatePaginate()`). 37 | 38 | ### MyModel.aggregatePaginate(aggregate, options, callback) 39 | 40 | **Arguments** 41 | 42 | * `aggregate` - An object of the [Mongoose][mongoose] aggregate. 43 | * `options` - An object with options for the [Mongoose][mongoose] query, such as sorting 44 | - `page` - Default: `1` 45 | - `limit` - Default: `10` 46 | - `sort` - Default: `undefined` 47 | * `callback(err, results, pages, total)` - A callback is called once pagination results are retrieved, or an error has occurred. If not specified promise will be returned 48 | 49 | **Returns** 50 | * `Promise` - Promise object 51 | 52 | **Examples** 53 | 54 | ```js 55 | 56 | let MyModel = mongoose.model('MyModel',{ 57 | name : String, 58 | age: Number, 59 | city, String 60 | }) 61 | 62 | // find users above 18 by city 63 | let aggregate = MyModel.aggregate(); 64 | aggregate.match({age : {'lt' : 18 } }) 65 | .group({ _id: '$city' , count : { '$sum' : 1 } }) 66 | let options = { page : 1, limit : 15} 67 | 68 | // callback 69 | MyModel.aggregatePaginate(aggregate, options, function(err, results, pages, count) { 70 | if(err) 71 | { 72 | console.err(err) 73 | } 74 | else 75 | { 76 | console.log(results) 77 | } 78 | }) 79 | 80 | // Promise 81 | MyModel.aggregatePaginate(aggregate, options) 82 | .then(function(value) { 83 | console.log(value.docs, value.pages, value.total) 84 | }) 85 | .catch(function(err){ 86 | console.err(err) 87 | }) 88 | ``` 89 | ## Tests 90 | 91 | ```js 92 | npm test 93 | ``` 94 | ## Acknowledgements 95 | mongoose-aggregate-paginate was inspired by [mongoose-paginate][mongoose-paginate]. 96 | 97 | ## License 98 | [MIT][license-url] 99 | 100 | [mongoose]: http://mongoosejs.com 101 | [mongoose-paginate]: https://www.npmjs.com/package/mongoose-paginate 102 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 103 | [license-url]: LICENSE 104 | [travis-ci-img]: https://travis-ci.org/Maheshkumar-Kakade/mongoose-aggregate-paginate.svg?branch=master 105 | [travis-ci-url]: https://travis-ci.org/Maheshkumar-Kakade/mongoose-aggregate-paginate 106 | [npm-version-img]: https://badge.fury.io/js/mongoose-aggregate-paginate.svg 107 | [npm-version-url]: http://badge.fury.io/js/mongoose-aggregate-paginate 108 | [dependency-status-img]: https://gemnasium.com/Maheshkumar-Kakade/mongoose-aggregate-paginate.svg 109 | [dependency-status-url]: https://gemnasium.com/Maheshkumar-Kakade/mongoose-aggregate-paginate 110 | [coveralls-image]: https://coveralls.io/repos/github/Maheshkumar-Kakade/mongoose-aggregate-paginate/badge.svg?branch=master 111 | [coveralls-url]: https://coveralls.io/github/Maheshkumar-Kakade/mongoose-aggregate-paginate?branch=master 112 | [js-standard-style-img]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg 113 | [js-standard-style-url]: http://standardjs.com/ -------------------------------------------------------------------------------- /test/all.tests.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | const mongoose = require('mongoose') 3 | require('should') 4 | const mongooseAggregatePaginate = require('../') 5 | mongoose.connect('mongodb://localhost/MongooseAggregatePaginate-test', { useNewUrlParser: true }).catch(error => { console.log(error) }) 6 | // mongoose.set('debug', true) 7 | const Schema = mongoose.Schema 8 | mongoose.Promise = Promise 9 | 10 | /** 11 | * test Schema 12 | */ 13 | const testSchema = new Schema( 14 | { 15 | studentId: Number, 16 | marksheet: [{ 17 | subject: String, 18 | marks: Number 19 | }] 20 | }, 21 | { 22 | timestamps: { createdAt: 'created', updatedAt: 'modified' } 23 | } 24 | ) 25 | testSchema.plugin(mongooseAggregatePaginate) 26 | 27 | const TestModel = mongoose.model('TestModel', testSchema, 'studentMarksheet') 28 | 29 | module.exports = testSchema 30 | 31 | describe('Mongoose Aggregate Paginate tests', function () { 32 | before(function (done) { 33 | const testData = [] 34 | for (let index = 0; index < 100; ++index) { 35 | testData.push(new TestModel({ 36 | studentId: index, 37 | marksheet: [{ subject: 'physics', marks: 100 - (index % 9) }, { 38 | subject: 'math', 39 | marks: 100 - (index % 8) 40 | }, { subject: 'chem', marks: 100 - (index % 7) }] 41 | })) 42 | } 43 | TestModel.deleteMany({}).then(() => TestModel.create(testData)) 44 | .then(function () { done() }) 45 | .catch(done) 46 | }) 47 | 48 | after(function (done) { 49 | mongoose.connection.close() 50 | done() 51 | }) 52 | 53 | describe('Basic Tests on 100 documents', function () { 54 | const query = TestModel.aggregate().allowDiskUse(true) 55 | .project({ marksheet: 1, studentId: 1 }) 56 | .unwind('$marksheet') 57 | .group({ _id: '$studentId', total: { $sum: '$marksheet.marks' } }) 58 | describe('without page and limit (callback)', function () { 59 | it('should return 10 results, page count = 10 and total count = 100', function (done) { 60 | TestModel.aggregatePaginate(query, {}, function (err, result, pages, total) { 61 | if (err) return done(err) 62 | result.length.should.equal(10) 63 | pages.should.equal(10) 64 | total.should.equal(100) 65 | done() 66 | }) 67 | }) 68 | }) 69 | 70 | describe('without page and limit (Promise)', function () { 71 | it('should return 10 results, page count = 10 and total count = 100', function (done) { 72 | TestModel.aggregatePaginate(query, {}) 73 | .then(function (value) { 74 | value.docs.length.should.equal(10) 75 | value.pages.should.equal(10) 76 | value.total.should.equal(100) 77 | done() 78 | }) 79 | .catch(done) 80 | }) 81 | }) 82 | 83 | describe('without page and limit using undefined as options param (callback)', function () { 84 | it('should return 10 results, page count = 10 and total count = 100', function (done) { 85 | TestModel.aggregatePaginate(query, undefined, function (err, result, pages, total) { 86 | if (err) return done(err) 87 | result.length.should.equal(10) 88 | pages.should.equal(10) 89 | total.should.equal(100) 90 | done() 91 | }) 92 | }) 93 | }) 94 | 95 | describe('without page and limit using undefined as options param (Promise)', function () { 96 | it('should return 10 results, page count = 10 and total count = 100', function (done) { 97 | TestModel.aggregatePaginate(query, undefined) 98 | .then(function (value) { 99 | value.docs.length.should.equal(10) 100 | value.pages.should.equal(10) 101 | value.total.should.equal(100) 102 | done() 103 | }) 104 | .catch(done) 105 | }) 106 | }) 107 | 108 | describe('with limit (callback)', function () { 109 | it('should return 20 results, page count = 5 and total count = 100 when limit is 20', function (done) { 110 | TestModel.aggregatePaginate(query, { limit: 20 }, function (err, result, pages, total) { 111 | if (err) return done(err) 112 | result.length.should.equal(20) 113 | pages.should.equal(5) 114 | total.should.equal(100) 115 | done() 116 | }) 117 | }) 118 | 119 | it('should return 5 results, page count = 20 and total count = 100 when limit is 5', function (done) { 120 | TestModel.aggregatePaginate(query, { limit: 5 }, function (err, result, pages, total) { 121 | if (err) return done(err) 122 | result.length.should.equal(5) 123 | pages.should.equal(20) 124 | total.should.equal(100) 125 | done() 126 | }) 127 | }) 128 | 129 | it('should return 100 results, page count = 1 and total count = 100 when limit is 200', function (done) { 130 | TestModel.aggregatePaginate(query, { limit: 200 }, function (err, result, pages, total) { 131 | if (err) return done(err) 132 | result.length.should.equal(100) 133 | pages.should.equal(1) 134 | total.should.equal(100) 135 | done() 136 | }) 137 | }) 138 | 139 | it('should return 0 results, page count = 1 and total count = 100 when limit = 200 and page = 2', function (done) { 140 | TestModel.aggregatePaginate(query, { limit: 200, page: 2 }, function (err, result, pages, total) { 141 | if (err) return done(err) 142 | result.length.should.equal(0) 143 | pages.should.equal(1) 144 | total.should.equal(100) 145 | done() 146 | }) 147 | }) 148 | 149 | it('should return 10 results, page count = 10 and total count = 100 when limit = 10 and page = 1 sort order = total desc', function (done) { 150 | // var sortQuery = TestModel.aggregate(query._pipeline) 151 | TestModel.aggregatePaginate(query, { 152 | limit: 10, 153 | page: 1, 154 | sort: { total: -1, _id: -1 } 155 | }, function (err, result, pages, total) { 156 | if (err) return done(err) 157 | result.length.should.equal(10) 158 | pages.should.equal(10) 159 | total.should.equal(100) 160 | result[0].total.should.equal(300) 161 | done() 162 | }) 163 | }) 164 | }) 165 | 166 | describe('with limit (Promise)', function () { 167 | it('should return 20 results, page count = 5 and total count = 100 when limit is 20', function (done) { 168 | TestModel.aggregatePaginate(query, { limit: 20 }) 169 | .then(function (value) { 170 | value.docs.length.should.equal(20) 171 | value.pages.should.equal(5) 172 | value.total.should.equal(100) 173 | done() 174 | }) 175 | .catch(done) 176 | }) 177 | 178 | it('should return 5 results, page count = 20 and total count = 100 when limit is 5', function (done) { 179 | TestModel.aggregatePaginate(query, { limit: 5 }) 180 | .then(function (value) { 181 | value.docs.length.should.equal(5) 182 | value.pages.should.equal(20) 183 | value.total.should.equal(100) 184 | done() 185 | }) 186 | .catch(done) 187 | }) 188 | 189 | it('should return 100 results, page count = 1 and total count = 100 when limit is 200', function (done) { 190 | TestModel.aggregatePaginate(query, { limit: 200 }) 191 | .then(function (value) { 192 | value.docs.length.should.equal(100) 193 | value.pages.should.equal(1) 194 | value.total.should.equal(100) 195 | done() 196 | }) 197 | .catch(done) 198 | }) 199 | 200 | it('should return 0 results, page count = 1 and total count = 100 when limit = 200 and page = 2', function (done) { 201 | TestModel.aggregatePaginate(query, { limit: 200, page: 2 }) 202 | .then(function (value) { 203 | value.docs.length.should.equal(0) 204 | value.pages.should.equal(1) 205 | value.total.should.equal(100) 206 | done() 207 | }) 208 | .catch(done) 209 | }) 210 | 211 | it('should return 10 results, page count = 10 and total count = 100 when limit = 10 and page = 1 sort order = total desc', function (done) { 212 | // var sortQuery = TestModel.aggregate(query._pipeline) 213 | TestModel.aggregatePaginate(query, { limit: 10, page: 1, sort: { total: -1, _id: -1 } }) 214 | .then(function (value) { 215 | value.docs.length.should.equal(10) 216 | value.pages.should.equal(10) 217 | value.total.should.equal(100) 218 | value.docs[0].total.should.equal(300) 219 | done() 220 | }) 221 | .catch(done) 222 | }) 223 | }) 224 | 225 | describe('with page (callback)', function () { 226 | it('should return 10 results, page count = 10 and total count = 100 when page = 1', function (done) { 227 | TestModel.aggregatePaginate(query, { page: 1 }, function (err, result, pages, total) { 228 | if (err) return done(err) 229 | result.length.should.equal(10) 230 | pages.should.equal(10) 231 | total.should.equal(100) 232 | done() 233 | }) 234 | }) 235 | 236 | it('should return 10 results, page count = 10 and total count = 100 when page = 2', function (done) { 237 | TestModel.aggregatePaginate(query, { page: 2 }, function (err, result, pages, total) { 238 | if (err) return done(err) 239 | result.length.should.equal(10) 240 | pages.should.equal(10) 241 | total.should.equal(100) 242 | done() 243 | }) 244 | }) 245 | 246 | it('should return 10 results, page count = 10 and total count = 100 when page = 10', function (done) { 247 | TestModel.aggregatePaginate(query, { page: 10 }, function (err, result, pages, total) { 248 | if (err) return done(err) 249 | result.length.should.equal(10) 250 | pages.should.equal(10) 251 | total.should.equal(100) 252 | done() 253 | }) 254 | }) 255 | 256 | it('should return 20 results, page count = 5 and total count = 100 when page = 5 , limit = 20 sort total desc', function (done) { 257 | TestModel.aggregatePaginate(query, { 258 | page: 5, 259 | limit: 20, 260 | sort: { total: -1 } 261 | }, function (err, result, pages, total) { 262 | if (err) return done(err) 263 | result.length.should.equal(20) 264 | pages.should.equal(5) 265 | total.should.equal(100) 266 | result[0].total.should.not.equal(300) 267 | done() 268 | }) 269 | }) 270 | 271 | it('should return error', function (done) { 272 | TestModel.aggregatePaginate(query, { 273 | page: -5, 274 | limit: 20, 275 | sort: { total: -1 } 276 | }, function (err, result, pages, total) { 277 | if (err) { 278 | err.should.should.not.equal(null) 279 | done() 280 | return 281 | } 282 | done('no error return') 283 | }) 284 | it('should return error', function (done) { 285 | const q = TestModel.aggregate() 286 | .project({ marksheet: 1, studentId: 1 }) 287 | .unwind('marksheet') 288 | .group({ _id: '$studentId', total: { $sum: '$marksheet.marks' } }) 289 | TestModel.aggregatePaginate(q, { 290 | page: -5, 291 | limit: 20, 292 | sort: { total: -1 } 293 | }, function (err, result, pages, total) { 294 | if (err) { 295 | err.should.should.not.equal(null) 296 | done() 297 | return 298 | } 299 | done('no error return') 300 | }) 301 | }) 302 | }) 303 | }) 304 | 305 | describe('with page (Promise)', function () { 306 | it('should return 10 results, page count = 10 and total count = 100 when page = 1', function (done) { 307 | TestModel.aggregatePaginate(query, { page: 1 }) 308 | .then(function (value) { 309 | value.docs.length.should.equal(10) 310 | value.pages.should.equal(10) 311 | value.total.should.equal(100) 312 | done() 313 | }) 314 | .catch(done) 315 | }) 316 | 317 | it('should return 10 results, page count = 10 and total count = 100 when page = 2', function (done) { 318 | TestModel.aggregatePaginate(query, { page: 2 }) 319 | .then(function (value) { 320 | value.docs.length.should.equal(10) 321 | value.pages.should.equal(10) 322 | value.total.should.equal(100) 323 | done() 324 | }) 325 | .catch(done) 326 | }) 327 | 328 | it('should return 10 results, page count = 10 and total count = 100 when page = 10', function (done) { 329 | TestModel.aggregatePaginate(query, { page: 10 }) 330 | .then(function (value) { 331 | value.docs.length.should.equal(10) 332 | value.pages.should.equal(10) 333 | value.total.should.equal(100) 334 | done() 335 | }) 336 | .catch(done) 337 | }) 338 | 339 | it('should return 20 results, page count = 5 and total count = 100 when page = 5 , limit = 20 sort total desc', function (done) { 340 | TestModel.aggregatePaginate(query, { page: 5, limit: 20, sort: { total: -1 } }) 341 | .then(function (value) { 342 | value.docs.length.should.equal(20) 343 | value.pages.should.equal(5) 344 | value.total.should.equal(100) 345 | value.docs[0].total.should.not.equal(300) 346 | done() 347 | }) 348 | .catch(done) 349 | }) 350 | 351 | it('should return error', function (done) { 352 | TestModel.aggregatePaginate(query, { page: -5, limit: 20, sort: { total: -1 } }) 353 | .then(function (value) { 354 | done('no error return') 355 | }) 356 | .catch(function (err) { 357 | err.should.should.not.equal(null) 358 | done() 359 | }) 360 | it('should return error', function (done) { 361 | const q = TestModel.aggregate() 362 | .project({ marksheet: 1, studentId: 1 }) 363 | .unwind('marksheet') 364 | .group({ _id: '$studentId', total: { $sum: '$marksheet.marks' } }) 365 | TestModel.aggregatePaginate(q, { page: -5, limit: 20, sort: { total: -1 } }) 366 | .then(function (value) { 367 | done('no error return') 368 | }) 369 | .catch(function (err) { 370 | err.should.should.not.equal(null) 371 | done() 372 | }) 373 | }) 374 | }) 375 | }) 376 | }) 377 | }) 378 | --------------------------------------------------------------------------------