diff --git a/plugins/mongoose-aggregate-paginate-v2/src/core.js b/plugins/mongoose-aggregate-paginate-v2/src/core.js index 5e8f9f3..456dcba 100644 --- a/plugins/mongoose-aggregate-paginate-v2/src/core.js +++ b/plugins/mongoose-aggregate-paginate-v2/src/core.js @@ -7,210 +7,215 @@ */ const 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, - limit: 10, - projection: {}, - select: "", - options: {}, - pagination: true, - countQuery: null, - useFacet: true + 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, + limit: 10, + projection: {}, + select: "", + options: {}, + pagination: true, + countQuery: null, + useFacet: true }; export const PREPAGINATION_PLACEHOLDER = "__PREPAGINATE__"; export function aggregatePaginate(query, options, callback) { - options = { - ...defaultOptions, - ...aggregatePaginate.options, - ...options - }; - - const pipeline = Array.isArray(query) ? query : query._pipeline; - - const customLabels = { - ...defaultOptions.customLabels, - ...options.customLabels - }; - - const defaultLimit = 10; - - // Custom Labels - const labelTotal = customLabels.totalDocs; - const labelLimit = customLabels.limit; - const labelPage = customLabels.page; - const labelTotalPages = customLabels.totalPages; - const labelDocs = customLabels.docs; - const labelNextPage = customLabels.nextPage; - const labelPrevPage = customLabels.prevPage; - const labelHasNextPage = customLabels.hasNextPage; - const labelHasPrevPage = customLabels.hasPrevPage; - const labelPagingCounter = customLabels.pagingCounter; - const labelMeta = customLabels.meta; - - let page = parseInt(options.page || 1, 10) || 1; - let limit = parseInt(options.limit, 10) > 0 ? parseInt(options.limit, 10) : defaultLimit; - - // const skip = (page - 1) * limit; - let skip; - let offset; - - if (Object.prototype.hasOwnProperty.call(options, "offset")) { - offset = Math.abs(parseInt(options.offset, 10)); - skip = offset; - } else if (Object.prototype.hasOwnProperty.call(options, "page")) { - page = Math.abs(parseInt(options.page, 10)) || 1; - skip = (page - 1) * limit; - } else { - offset = 0; - page = 1; - skip = offset; - } - - const sort = options.sort; - const allowDiskUse = options.allowDiskUse || false; - const isPaginationEnabled = options.pagination === false ? false : true; - - const q = this.aggregate(); - - if (allowDiskUse) { - q.allowDiskUse(true); - } - - if (sort) { - pipeline.push({ $sort: sort }); - } - - function constructPipelines() { - let cleanedPipeline = pipeline.filter((stage) => stage !== PREPAGINATION_PLACEHOLDER); - - const countPipeline = [...cleanedPipeline, { $count: "count" }]; - - if (isPaginationEnabled) { - cleanedPipeline = pipeline.flatMap((stage) => { - if (stage === PREPAGINATION_PLACEHOLDER) { - return [{ $skip: skip }, { $limit: limit }]; - } - return stage; - }); + options = { + ...defaultOptions, + ...aggregatePaginate.options, + ...options + }; + + const pipeline = Array.isArray(query) ? query : query._pipeline; + + const customLabels = { + ...defaultOptions.customLabels, + ...options.customLabels + }; + + const defaultLimit = 10; + + // Custom Labels + const labelTotal = customLabels.totalDocs; + const labelLimit = customLabels.limit; + const labelPage = customLabels.page; + const labelTotalPages = customLabels.totalPages; + const labelDocs = customLabels.docs; + const labelNextPage = customLabels.nextPage; + const labelPrevPage = customLabels.prevPage; + const labelHasNextPage = customLabels.hasNextPage; + const labelHasPrevPage = customLabels.hasPrevPage; + const labelPagingCounter = customLabels.pagingCounter; + const labelMeta = customLabels.meta; + + let page = parseInt(options.page || 1, 10) || 1; + let limit = parseInt(options.limit, 10) > 0 ? parseInt(options.limit, 10) : defaultLimit; + + // const skip = (page - 1) * limit; + let skip; + let offset; + + if (Object.prototype.hasOwnProperty.call(options, "offset")) { + offset = Math.abs(parseInt(options.offset, 10)); + skip = offset; + } else if (Object.prototype.hasOwnProperty.call(options, "page")) { + page = Math.abs(parseInt(options.page, 10)) || 1; + skip = (page - 1) * limit; + } else { + offset = 0; + page = 1; + skip = offset; } - return [cleanedPipeline, countPipeline]; - } - let promise; + const sort = options.sort; + const allowDiskUse = options.allowDiskUse || false; + const isPaginationEnabled = options.pagination === false ? false : true; - if (options.useFacet && !options.countQuery) { - const [pipeline, countPipeline] = constructPipelines(); - promise = q - .facet({ - docs: pipeline, - count: countPipeline - }) - .then(([{ docs, count }]) => [docs, count]); - } else { - const [pipeline] = constructPipelines(); - - const countQuery = options.countQuery ? options.countQuery : this.aggregate(pipeline); + const q = this.aggregate(); if (allowDiskUse) { - countQuery.allowDiskUse(true); + q.allowDiskUse(true); } - promise = Promise.all([ - this.aggregate(pipeline).exec(), - countQuery - .group({ - _id: null, - count: { - $sum: 1 - } - }) - .exec() - ]); - } + if (sort) { + pipeline.push({ $sort: sort }); + } - return promise - .then(function (values) { - const count = values[1][0] ? values[1][0].count : 0; + function constructPipelines() { + let cleanedPipeline = pipeline.filter((stage) => stage !== PREPAGINATION_PLACEHOLDER); + + const countPipeline = [...cleanedPipeline, { $count: "count" }]; + + if (isPaginationEnabled) { + let foundPrepagination = false; + cleanedPipeline = pipeline.flatMap((stage) => { + if (stage === PREPAGINATION_PLACEHOLDER) { + foundPrepagination = true; + return [{ $skip: skip }, { $limit: limit }]; + } + return stage; + }); + if (!foundPrepagination) { + cleanedPipeline.push({ $skip: skip }, { $limit: limit }); + } + } + return [cleanedPipeline, countPipeline]; + } - if (isPaginationEnabled === false) { - limit = count; - page = 1; - } - - const pages = Math.ceil(count / limit) || 1; - - let result = { - [labelDocs]: values[0] - }; - - const meta = { - [labelTotal]: count, - [labelLimit]: limit, - [labelPage]: page, - [labelTotalPages]: pages, - [labelPagingCounter]: (page - 1) * limit + 1, - [labelHasPrevPage]: false, - [labelHasNextPage]: false - }; - - if (typeof offset !== "undefined") { - page = Math.ceil((offset + 1) / limit); - - meta.offset = offset; - meta[labelPage] = Math.ceil((offset + 1) / limit); - meta[labelPagingCounter] = offset + 1; - } - - // Set prev page - if (page > 1) { - meta[labelHasPrevPage] = true; - meta[labelPrevPage] = page - 1; - } else { - meta[labelPrevPage] = null; - } - - // Set next page - if (page < pages) { - meta[labelHasNextPage] = true; - meta[labelNextPage] = page + 1; - } else { - meta[labelNextPage] = null; - } - - if (labelMeta) { - result[labelMeta] = meta; - } else { - result = Object.assign(result, meta); - } - - if (typeof callback === "function") { - return callback(null, result); - } - - return Promise.resolve(result); - }) - .catch(function (reject) { - if (typeof callback === "function") { - return callback(reject); - } - return Promise.reject(reject); - }); + let promise; + + if (options.useFacet && !options.countQuery) { + const [pipeline, countPipeline] = constructPipelines(); + promise = q + .facet({ + docs: pipeline, + count: countPipeline + }) + .then(([{ docs, count }]) => [docs, count]); + } else { + const [pipeline] = constructPipelines(); + + const countQuery = options.countQuery ? options.countQuery : this.aggregate(pipeline); + + if (allowDiskUse) { + countQuery.allowDiskUse(true); + } + + promise = Promise.all([ + this.aggregate(pipeline).exec(), + countQuery + .group({ + _id: null, + count: { + $sum: 1 + } + }) + .exec() + ]); + } + + return promise + .then(function (values) { + const count = values[1][0] ? values[1][0].count : 0; + + if (isPaginationEnabled === false) { + limit = count; + page = 1; + } + + const pages = Math.ceil(count / limit) || 1; + + let result = { + [labelDocs]: values[0] + }; + + const meta = { + [labelTotal]: count, + [labelLimit]: limit, + [labelPage]: page, + [labelTotalPages]: pages, + [labelPagingCounter]: (page - 1) * limit + 1, + [labelHasPrevPage]: false, + [labelHasNextPage]: false + }; + + if (typeof offset !== "undefined") { + page = Math.ceil((offset + 1) / limit); + + meta.offset = offset; + meta[labelPage] = Math.ceil((offset + 1) / limit); + meta[labelPagingCounter] = offset + 1; + } + + // Set prev page + if (page > 1) { + meta[labelHasPrevPage] = true; + meta[labelPrevPage] = page - 1; + } else { + meta[labelPrevPage] = null; + } + + // Set next page + if (page < pages) { + meta[labelHasNextPage] = true; + meta[labelNextPage] = page + 1; + } else { + meta[labelNextPage] = null; + } + + if (labelMeta) { + result[labelMeta] = meta; + } else { + result = Object.assign(result, meta); + } + + if (typeof callback === "function") { + return callback(null, result); + } + + return Promise.resolve(result); + }) + .catch(function (reject) { + if (typeof callback === "function") { + return callback(reject); + } + return Promise.reject(reject); + }); } export default aggregatePaginate; diff --git a/plugins/mongoose-aggregate-paginate-v2/test/index.test.js b/plugins/mongoose-aggregate-paginate-v2/test/index.test.js index fcd2a1e..d779fb8 100644 --- a/plugins/mongoose-aggregate-paginate-v2/test/index.test.js +++ b/plugins/mongoose-aggregate-paginate-v2/test/index.test.js @@ -64,7 +64,7 @@ describe("mongoose-paginate", function () { let promise = aggregate.paginateExec({}); // let promise = Book.aggregatePaginate(aggregate, {}); - expect(promise.then).to.be.an.instanceof(Function); + expect(promise.then).toBeInstanceOf(Function); }); it("callback test", function (done) { @@ -79,8 +79,8 @@ describe("mongoose-paginate", function () { ]); aggregate.paginateExec({}, function (err, result) { - expect(err).to.be.null; - expect(result).to.be.an.instanceOf(Object); + expect(err).toBeNull(); + expect(result).toBeInstanceOf(Object); done(); }); }); @@ -112,7 +112,7 @@ describe("mongoose-paginate", function () { ]) }; return Book.aggregatePaginate(aggregate, options).then((result) => { - expect(result.docs).to.have.length(10); + expect(result.docs).toHaveLength(10); expect(result.docs[0].title).toEqual("Book #41"); expect(result.totalDocs).toEqual(100); expect(result.limit).toEqual(10); @@ -152,7 +152,7 @@ describe("mongoose-paginate", function () { allowDiskUse: true }; return Book.aggregatePaginate(aggregate, options).then((result) => { - expect(result.docs).to.have.length(10); + expect(result.docs).toHaveLength(10); expect(result.docs[0].title).toEqual("Book #41"); expect(result.totalDocs).toEqual(100); expect(result.limit).toEqual(10);