UNPKG

nestjs-prisma-base

Version:

A comprehensive NestJS package providing base classes, utilities, and decorators for building CRUD APIs with Prisma ORM integration, featuring pagination, search, filtering, relation loading, configurable DTOs, and modular composition capabilities.

174 lines 7.28 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AdvancedQueryBuilder = void 0; const common_1 = require("@nestjs/common"); class AdvancedQueryBuilder { static buildQuery(options, searchConfig) { const result = {}; result.where = this.buildWhereConditions(options, searchConfig); if (options.include) { result.include = options.include; } if (options.select) { result.select = options.select; } if (options.orderBy) { result.orderBy = this.buildOrderConditions(options.orderBy); } return result; } static buildWhereConditions(options, searchConfig) { const conditions = []; if (options.search && options.search.trim()) { const searchCondition = this.buildBasicSearchCondition(options.search.trim(), options.searchFields || searchConfig.defaultSearchFields, searchConfig); if (searchCondition) { conditions.push(searchCondition); } } if (options.filters && Object.keys(options.filters).length > 0) { const filterCondition = this.buildSimpleFilters(options.filters); if (filterCondition) { conditions.push(filterCondition); } } if (options.advancedFilters && Object.keys(options.advancedFilters).length > 0) { this.validateAdvancedFilters(options.advancedFilters, searchConfig); const advancedCondition = this.buildAdvancedFilters(options.advancedFilters); if (advancedCondition) { conditions.push(advancedCondition); } } if (options.where) { conditions.push(options.where); } if (conditions.length === 0) { return undefined; } if (conditions.length === 1) { return conditions[0]; } const logicalOp = options.logicalOperator || 'AND'; return { [logicalOp]: conditions, }; } static buildBasicSearchCondition(searchTerm, searchFields, searchConfig) { if (searchFields.length === 0) { return undefined; } const searchConditions = searchFields.map((field) => { const condition = {}; switch (searchConfig.searchMode) { case 'startsWith': condition[field] = { startsWith: searchTerm, mode: searchConfig.caseSensitive ? 'default' : 'insensitive', }; break; case 'endsWith': condition[field] = { endsWith: searchTerm, mode: searchConfig.caseSensitive ? 'default' : 'insensitive', }; break; case 'contains': default: condition[field] = { contains: searchTerm, mode: searchConfig.caseSensitive ? 'default' : 'insensitive', }; break; } return condition; }); return { OR: searchConditions }; } static buildSimpleFilters(filters) { const filterConditions = {}; Object.entries(filters).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '') { filterConditions[key] = value; } }); return Object.keys(filterConditions).length > 0 ? filterConditions : undefined; } static buildAdvancedFilters(advancedFilters) { const conditions = {}; Object.entries(advancedFilters).forEach(([field, filter]) => { const condition = this.buildOperatorCondition(filter.operator, filter.value); if (condition !== undefined) { conditions[field] = condition; } }); return Object.keys(conditions).length > 0 ? conditions : undefined; } static buildOperatorCondition(operator, value) { switch (operator) { case 'equals': return value; case 'not': return { not: value }; case 'contains': return { contains: value, mode: 'insensitive' }; case 'startsWith': return { startsWith: value, mode: 'insensitive' }; case 'endsWith': return { endsWith: value, mode: 'insensitive' }; case 'gt': return { gt: value }; case 'gte': return { gte: value }; case 'lt': return { lt: value }; case 'lte': return { lte: value }; case 'in': return { in: Array.isArray(value) ? value : [value] }; case 'notIn': return { notIn: Array.isArray(value) ? value : [value] }; case 'isNull': return null; case 'isNotNull': return { not: null }; default: throw new common_1.BadRequestException(`Unsupported operator: ${operator}`); } } static buildOrderConditions(orderBy) { return Object.entries(orderBy).map(([field, direction]) => ({ [field]: direction, })); } static validateAdvancedFilters(advancedFilters, searchConfig) { const filterCount = Object.keys(advancedFilters).length; const maxFilters = searchConfig.maxAdvancedFilters || 20; if (filterCount > maxFilters) { throw new common_1.BadRequestException(`Too many advanced filters. Maximum allowed: ${maxFilters}, provided: ${filterCount}`); } if (searchConfig.allowedAdvancedFields && searchConfig.allowedAdvancedFields.length > 0) { const invalidFields = Object.keys(advancedFilters).filter((field) => !searchConfig.allowedAdvancedFields.includes(field)); if (invalidFields.length > 0) { throw new common_1.BadRequestException(`Invalid advanced filter fields: ${invalidFields.join(', ')}. ` + `Allowed fields: ${searchConfig.allowedAdvancedFields.join(', ')}`); } } Object.entries(advancedFilters).forEach(([field, filter]) => { this.validateOperatorValue(field, filter.operator, filter.value); }); } static validateOperatorValue(field, operator, value) { const nullOperators = ['isNull', 'isNotNull']; const arrayOperators = ['in', 'notIn']; if (nullOperators.includes(operator)) { return; } if (value === undefined || value === null) { throw new common_1.BadRequestException(`Advanced filter for field '${field}' with operator '${operator}' requires a value`); } if (arrayOperators.includes(operator) && !Array.isArray(value)) { throw new common_1.BadRequestException(`Advanced filter for field '${field}' with operator '${operator}' requires an array value`); } } } exports.AdvancedQueryBuilder = AdvancedQueryBuilder; //# sourceMappingURL=query-builder.js.map