UNPKG

@blogcode/nest-typeorm-pagination

Version:
285 lines (284 loc) 13.2 kB
"use strict"; var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; }; var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) { if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PaginationService = void 0; const common_1 = require("@nestjs/common"); const utils_1 = require("./utils"); const operators_1 = require("./operators"); let PaginationService = (() => { let _classDecorators = [(0, common_1.Injectable)()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var PaginationService = _classThis = class { constructor(dataSource) { this.dataSource = dataSource; this.filterMapping = {}; this.defaultLimit = utils_1.DEFAULT_LIMIT; this.maxLimit = utils_1.MAX_LIMIT; this.resultName = 'data'; this.primaryKey = 'id'; } setFilterMapping(mapping) { this.filterMapping = Object.assign({}, mapping); return this; } setDefaultLimit(limit) { if (limit <= 0) { return this; } this.defaultLimit = limit; return this; } setMaxLimit(limit) { if (limit <= 0) { return this; } this.maxLimit = limit; return this; } setEntityName(name) { this.entityName = name; return this; } setResultName(name) { if (!name) { return this; } this.resultName = name; return this; } setPrimaryKey(key) { if (!key) { return this; } this.primaryKey = key; return this; } getOperator(operator) { if (!operator) { return '='; } return operators_1.operators[operator.toLowerCase()] || '='; } getLimit(limit) { return limit && limit > 0 && limit <= this.maxLimit ? limit : this.defaultLimit; } parseValue(value, operator) { var _a; if (['like', 'ilike', 'nlike', 'nilike'].includes((_a = operator === null || operator === void 0 ? void 0 : operator.toLowerCase()) !== null && _a !== void 0 ? _a : '')) { return `%${value}%`; } return value; } genCondition(operator, dbField, key, isArray = false) { const ope = this.getOperator(operator); const arrFields = dbField.split(',').filter((field) => field.trim()).map((field) => { if (['BETWEEN', 'NOT BETWEEN'].includes(ope)) { if (isArray) { return `${this.normalize(field)} ${ope} :${key}Start AND :${key}End`; } const o = ope === 'BETWEEN' ? '=' : '!='; return `${this.normalize(field)} ${o} :${key}`; } return `${this.normalize(field)} ${ope} :${key}`; }); if (arrFields.length === 1) { return arrFields[0]; } return `(${arrFields.join(' OR ')})`; } getWrapper() { switch (this.dataSource.options.type) { case 'postgres': return '"'; case 'mysql': case 'mariadb': return '`'; default: return ''; } } normalize(name) { const w = this.getWrapper(); return `${w}${name}${w}`; } genFullTextSearch(dbField, key) { switch (this.dataSource.options.type) { case 'postgres': return `${key} @@ ${this.normalize(dbField)}`; case 'mysql': case 'mariadb': return `MATCH (${this.normalize(dbField)}) AGAINST (:${key} IN BOOLEAN MODE)`; default: return ''; } } applyFilterCondition(query, dbField, key, filter) { if ((0, utils_1.isBaseType)(filter)) { if (filter === null) { query.andWhere(`${this.normalize(dbField)} IS NULL`); return; } query.andWhere(this.genCondition('=', dbField, key), { [key]: filter }); return; } const { value, operator } = filter; if (value === null) { if (!operator || (operator === null || operator === void 0 ? void 0 : operator.toLowerCase()) === 'is' || operator === "=") { query.andWhere(`${this.normalize(dbField)} IS NULL`); return; } if ((operator === null || operator === void 0 ? void 0 : operator.toLowerCase()) === 'isnot' || operator === "!=") { query.andWhere(`${this.normalize(dbField)} IS NOT NULL`); return; } return; } if ((operator === null || operator === void 0 ? void 0 : operator.toLowerCase()) === 'fts') { query.andWhere(this.genFullTextSearch(dbField, key), { [key]: value }); } const params = { [`${key}Start`]: Array.isArray(value) && value.length === 2 ? value[0] : undefined, [`${key}End`]: Array.isArray(value) && value.length === 2 ? value[1] : undefined, [key]: this.parseValue(Array.isArray(value) && value.length === 1 ? value[0] : value, operator), }; query.andWhere(this.genCondition(operator || "=", dbField, key, Array.isArray(value)), params); } applyFilters(query, filters) { if (!filters) { return; } Object.keys(filters).forEach((key) => { const dbField = this.filterMapping[key] || key; this.applyFilterCondition(query, dbField, key, filters[key]); }); } applyOrder(query, order) { Object.entries(order || {}).forEach(([field, direction]) => { const dbField = this.filterMapping[field] || field; query.addOrderBy(dbField, +direction === 1 ? 'ASC' : 'DESC'); }); } offset(repository, dto) { return __awaiter(this, void 0, void 0, function* () { const { page = 1, limit, filters, order } = dto; const entityName = this.entityName || repository.metadata.tableName; const query = repository.createQueryBuilder(entityName); const latestLimit = this.getLimit(+limit); const resultName = this.resultName; this.applyFilters(query, filters); this.applyOrder(query, order || {}); const [result, total] = yield query .skip((page - 1) * latestLimit) .take(latestLimit) .getManyAndCount(); return { [resultName]: result, total, page, limit: latestLimit, total_page: Math.ceil(total / latestLimit), }; }); } cursor(repository, dto) { return __awaiter(this, void 0, void 0, function* () { const { cursor, limit, filters, order } = dto; const entityName = this.entityName || repository.metadata.tableName; const query = repository.createQueryBuilder(entityName); const direction = (0, utils_1.getCursorDirection)(dto); const primary = this.primaryKey; this.applyFilters(query, filters); const w = this.getWrapper(); if (cursor) { const comparisonOperator = direction === 'next' ? '>' : '<'; query.andWhere(`${w}${entityName}${w}.${w}${primary}${w} ${comparisonOperator} :cursor`, { cursor, }); } const latestLimit = this.getLimit(+limit); const resultName = this.resultName; query.take(latestLimit); this.applyOrder(query, order); query.orderBy(`${entityName}.${primary}`, direction === 'next' ? 'ASC' : 'DESC'); const result = yield query.getMany(); const nextCursor = result.length > 0 ? result[result.length - 1] : null; const prevCursor = result.length > 0 ? result[0] : null; return { [resultName]: result, nextCursor, prevCursor, direction, }; }); } getCursorTotal(repository, dto) { return __awaiter(this, void 0, void 0, function* () { const { filters } = dto; const entityName = this.entityName || repository.metadata.tableName; const query = repository.createQueryBuilder(entityName); this.applyFilters(query, filters); return yield query.getCount(); }); } }; __setFunctionName(_classThis, "PaginationService"); (() => { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); PaginationService = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); })(); return PaginationService = _classThis; })(); exports.PaginationService = PaginationService;