UNPKG

@amirmarmul/waba-common

Version:

![GitHub release](https://img.shields.io/github/v/release/amirmarmul/waba-common?style=flat-square)

216 lines (215 loc) 8.69 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.mongooseCursorPaginate = exports.CursorPaginationModel = void 0; const core_1 = require("../../core"); const moment_1 = __importDefault(require("moment")); const mongoose_1 = __importDefault(require("mongoose")); class CursorPaginationModel { meta = { limit: 0 }; docs = []; } exports.CursorPaginationModel = CursorPaginationModel; function mongooseCursorPaginate(schema) { schema.statics.cursor = async function cursor(options, onError) { let baseQuery = options?.query ? { ...options.query } : {}; let limit = parseInt(options?.limit ?? '10'); let fetchLimit = limit + 1; let direction = 'next'; let sort = options?.sort || (options?.aggregate?.find((agg) => agg.$sort) || {}).$sort || { _id: -1 }; if (options?.cursor) { try { const decodedStr = core_1.Crypto.decrypt(options.cursor); const decoded = JSON.parse(decodedStr); core_1.logger.debug({ decoded }); direction = decoded.direction; const sortQuery = decoded.query?.sort || {}; delete decoded.query?.sort; for (const key of Object.keys(sort)) { let value = sortQuery[key]; if (key.endsWith('At') && typeof value === 'string') { value = (0, moment_1.default)(value).toDate(); } if (value !== undefined) { const operator = direction === 'next' ? (sort[key] === 1 ? '$gte' : '$lte') : (sort[key] === 1 ? '$lte' : '$gte'); baseQuery.$or = [ { [key]: { [operator]: value } }, { [key]: { $exists: false } }, { [key]: null } ]; if (decoded.query?.[key]) { baseQuery.$or = baseQuery.$or.map((condition) => ({ ...condition, ...decoded.query[key] })); } } } core_1.logger.debug({ baseQuery }); baseQuery = { ...baseQuery, ...decoded.query }; } catch (e) { if (onError) onError(e); core_1.logger.debug({ e, stack: e.stack }); return undefined; } } if (baseQuery && baseQuery._id) { ['gte', 'lte', 'ne'].forEach(op => { if (baseQuery._id[`$${op}`] && typeof baseQuery._id[`$${op}`] === 'string') { baseQuery._id[`$${op}`] = new mongoose_1.default.Types.ObjectId(baseQuery._id[`$${op}`]); } }); } let effectiveQuery = { ...baseQuery }; if (options?.search && options.search.value && options.search.fields && options.search.fields.length) { const searchQuery = { $regex: options.search.value, $options: 'i', }; if (options.search.fields.length === 1) { effectiveQuery[options.search.fields[0]] = searchQuery; } else { effectiveQuery.$or = options.search.fields.map(field => ({ [field]: searchQuery })); } } if (direction === 'previous') { sort = Object.fromEntries(Object.entries(sort).map(([key, value]) => [key, -value])); } else { sort = Object.fromEntries(Object.entries(sort).map(([key, value]) => [key, parseInt(value)])); } const projection = options?.projection ?? {}; const populate = options?.populate; core_1.logger.debug({ effectiveQuery }); let mQuery; if (options?.aggregate) { const aggregatePipeline = [...options.aggregate]; const matchIndex = aggregatePipeline.findIndex(stage => stage.$match); if (matchIndex !== -1) { aggregatePipeline[matchIndex].$match = { ...aggregatePipeline[matchIndex].$match, ...effectiveQuery }; } else if (Object.keys(effectiveQuery).length > 0) { aggregatePipeline.unshift({ $match: effectiveQuery }); } const sortIndex = aggregatePipeline.findIndex(stage => stage.$sort); if (sortIndex !== -1) { aggregatePipeline[sortIndex].$sort = sort; } else { aggregatePipeline.unshift({ $sort: sort }); } const limitIndex = aggregatePipeline.findIndex(stage => stage.$limit); if (limitIndex !== -1) { aggregatePipeline[limitIndex].$limit = fetchLimit; } else { aggregatePipeline.unshift({ $limit: fetchLimit }); } core_1.logger.debug({ aggregatePipeline }); mQuery = this.aggregate(aggregatePipeline); if (options.select) { mQuery = mQuery.project(options.select); } } else { mQuery = this.find(effectiveQuery, projection); if (options?.select) { mQuery = mQuery.select(options.select); } if (populate) { mQuery = mQuery.populate(populate); } mQuery = mQuery.sort(sort).limit(fetchLimit); } try { let docs = await mQuery.exec(); let hasMore = docs.length > fetchLimit - 1; if (direction === 'next') { if (hasMore) { docs.pop(); } } else if (direction === 'previous') { if (hasMore) { docs.pop(); } docs = docs.reverse(); } const sortKeys = Object.keys(sort); let newCursorNext = undefined; let newCursorPrevious = undefined; if (docs.length > 0) { const lastDoc = docs[docs.length - 1]; const firstDoc = docs[0]; const buildSortValues = (doc) => { return sortKeys.reduce((acc, key) => { acc[key] = doc[key]; return acc; }, {}); }; if ((direction === 'next' && hasMore) || direction === 'previous') { newCursorNext = core_1.Crypto.encrypt(JSON.stringify({ query: { _id: { $ne: lastDoc._id }, sort: buildSortValues(lastDoc) }, direction: 'next' })); } if (options?.cursor) { if (direction === 'next' || (direction === 'previous' && hasMore)) { newCursorPrevious = core_1.Crypto.encrypt(JSON.stringify({ query: { _id: { $ne: firstDoc._id }, sort: buildSortValues(firstDoc) }, direction: 'previous' })); } } } let cursors = { next: newCursorNext, previous: newCursorPrevious }; if (!newCursorNext && !newCursorPrevious) { cursors = undefined; } const result = new CursorPaginationModel(); result.meta = { limit, cursors }; result.docs = docs; return result; } catch (error) { if (onError) onError(error); core_1.logger.debug({ error, stack: error.stack }); return undefined; } }; const toJSONOptions = { virtuals: true, transform: function (doc, ret) { ret.id = ret._id; delete ret.__v; } }; schema.set('toJSON', toJSONOptions); schema.set('toObject', toJSONOptions); } exports.mongooseCursorPaginate = mongooseCursorPaginate;