UNPKG

iagate-querykit

Version:

QueryKit: lightweight TypeScript query toolkit with models, views, triggers, events, scheduler and adapters (better-sqlite3).

1,319 lines 333 kB
/** * Operadores SQL suportados para comparações em cláusulas WHERE. * Inclui operadores de igualdade, comparação, padrão e verificação de valores nulos. */ export type Operator = '=' | '!=' | '<>' | '>' | '>=' | '<' | '<=' | 'LIKE' | 'NOT LIKE' | 'IN' | 'NOT IN' | 'BETWEEN' | 'NOT BETWEEN' | 'IS NULL' | 'IS NOT NULL'; /** * Seletor de relacionamentos para carregamento eager de dados relacionados. * Permite especificar quais relacionamentos carregar e quais colunas selecionar. * * @template T - Tipo genérico da entidade principal */ export type RelationshipSelector<T> = (rel: (name: string, select?: string[]) => void) => void; /** * QueryBuilder - Construtor de consultas SQL fluente e type-safe. * * Esta classe fornece uma interface fluente para construir consultas SQL complexas * com suporte completo a operações CRUD, relacionamentos, agregações e simulação. * * Características principais: * - API fluente com method chaining * - Suporte completo a operadores SQL * - Sistema de eventos para hooks antes/depois das operações * - Simulação de queries para desenvolvimento e testes * - Suporte a múltiplos bancos de dados * - Carregamento automático de relacionamentos * - Geração de SQL parametrizado com bindings * * @template T - Tipo da entidade da tabela, deve estender Record<string, any> e opcionalmente ter um campo 'id' * * @example * // Exemplo básico de uso * const users = await new QueryBuilder<User>('users') * .select(['id', 'name', 'email']) * .where('active', '=', true) * .where('age', '>', 18) * .orderBy('name', 'ASC') * .limit(10) * .all(); * * @example * // Exemplo com relacionamentos * const posts = await new QueryBuilder<Post>('posts') * .where('published', '=', true) * .relationship(rel => { * rel('author', ['id', 'name']); * rel('comments', ['id', 'content']); * }) * .all(); * * @example * // Exemplo de operação de escrita * const result = await new QueryBuilder<User>('users') * .where('id', '=', 1) * .update({ lastLogin: new Date() }) * .make(); */ export declare class QueryBuilder<T extends { id?: any; } & Record<string, any>> { private tableName; private whereClauses; private orWhereClauses; private joins; private selectColumns; private orderClauses; private limitValue?; private offsetValue?; private groupByColumns; private havingClauses; private isDistinct; private pendingAction?; private aggregates; private tableAlias?; private unionParts; private targetBanks?; private isTracking; private isSeeding; private trackingLogs; private virtualTable; private includeAllRelations; /** * Construtor da classe QueryBuilder. * Inicializa uma nova instância para construir consultas na tabela especificada. * * @param tableName - Nome da tabela alvo para as consultas * * @example * // Exemplo básico - Inicialização simples * const query = new QueryBuilder<User>('users'); * * @example * // Exemplo com tabela customizada * const query = new QueryBuilder<Order>('customer_orders'); * * @example * // Exemplo para tabela de sistema * const query = new QueryBuilder<Log>('system_logs'); */ constructor(tableName: string); /** * Define o banco de dados de destino para a query. * Permite especificar qual banco de dados usar quando há múltiplos bancos configurados. * * @param bankOrBanks - Nome do banco ou array de nomes de bancos * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Banco único * const query = new QueryBuilder<User>('users') * .bank('postgres') * .where('active', '=', true); * * @example * // Exemplo intermediário - Múltiplos bancos * const query = new QueryBuilder<User>('users') * .bank(['postgres', 'mysql']) * .where('created_at', '>', new Date('2024-01-01')); * * @example * // Exemplo avançado - Sistema multi-banco com fallback * class MultiDatabaseService { * static async getUserFromMultipleSources(userId: number): Promise<User[]> { * const primaryQuery = new QueryBuilder<User>('users') * .bank('postgres') * .where('id', '=', userId); * * const backupQuery = new QueryBuilder<User>('users') * .bank('mysql') * .where('id', '=', userId); * * try { * const primaryResult = await primaryQuery.all(); * if (primaryResult.length > 0) return primaryResult; * } catch (error) { * console.log('Falha no banco primário, tentando backup...'); * } * * return await backupQuery.all(); * } * } */ bank(bankOrBanks: string | string[]): this; /** * Verifica se existe uma operação de escrita pendente na instância atual. * Útil para validação antes de executar operações ou para debugging. * * @returns true se há uma operação de escrita pendente, false caso contrário * * @example * // Exemplo básico - Verificação simples * const query = new QueryBuilder<User>('users').insert({ name: 'John' }); * if (query.hasPendingWrite()) { * console.log('Query tem operação pendente'); * } * * @example * // Exemplo intermediário - Validação antes de executar * const query = new QueryBuilder<User>('users') * .where('id', '=', 1) * .update({ lastLogin: new Date() }); * * if (!query.hasPendingWrite()) { * throw new Error('Nenhuma operação de escrita configurada'); * } * * @example * // Exemplo avançado - Sistema de validação complexo * class QueryValidator { * static validateBeforeExecution(query: QueryBuilder<any>): void { * if (query.hasPendingWrite()) { * const action = query['pendingAction']; * if (action?.type === 'delete' && !query['whereClauses'].length) { * throw new Error('DELETE sem WHERE clause é perigoso!'); * } * } * } * } */ hasPendingWrite(): boolean; /** * Método privado para registrar logs de tracking durante a execução da query. * Utilizado internamente para monitorar o comportamento das operações. * * @param step - Nome da etapa sendo executada * @param details - Detalhes adicionais da operação */ private track; /** * Inicializa o sistema de tracking para monitorar a execução da query. * Permite simular operações em memória ou rastrear execuções reais no banco. * * @param data - Dados opcionais para inicializar a tabela virtual (para simulação) * @returns Promise que resolve para a instância atual do QueryBuilder * * @example * // Exemplo básico - Inicialização com dados existentes * const query = new QueryBuilder<User>('users') * .initial([ * { id: 1, name: 'John', email: 'john@example.com' }, * { id: 2, name: 'Jane', email: 'jane@example.com' } * ]); * * @example * // Exemplo intermediário - Tracking de query real * const query = new QueryBuilder<User>('users') * .where('active', '=', true) * .await initial(); // Carrega dados reais do banco * * @example * // Exemplo avançado - Sistema de simulação complexo * class QuerySimulator { * async simulateUserOperations(): Promise<void> { * const query = new QueryBuilder<User>('users') * .where('role', '=', 'admin') * .await initial(); // Carrega dados reais * * // Simula operações complexas * query.insert({ name: 'New Admin', role: 'admin' }); * query.where('id', '=', 1).update({ lastLogin: new Date() }); * * const logs = query.tracking(); * console.log('Operações simuladas:', logs); * } * } */ initial(data?: T[]): Promise<this>; /** * Retorna os logs de tracking das operações executadas na instância. * Deve ser chamado após .initial() para funcionar corretamente. * * @returns Array de logs com detalhes de cada operação executada * * @example * // Exemplo básico - Obter logs de tracking * const query = new QueryBuilder<User>('users') * .await initial() * .insert({ name: 'John' }); * * const logs = query.tracking(); * console.log('Logs:', logs); * * @example * // Exemplo intermediário - Análise detalhada dos logs * const query = new QueryBuilder<User>('users') * .await initial() * .where('id', '=', 1) * .update({ lastLogin: new Date() }); * * const logs = query.tracking(); * const updateLogs = logs.filter(log => log.step.includes('update')); * console.log('Logs de atualização:', updateLogs); * * @example * // Exemplo avançado - Sistema de auditoria completo * class QueryAuditor { * async auditQuery(query: QueryBuilder<any>): Promise<AuditReport> { * const logs = query.tracking(); * * const report: AuditReport = { * totalOperations: logs.length, * operations: logs.map(log => ({ * step: log.step, * timestamp: log.timestamp, * details: log.details * })), * hasWrites: logs.some(log => * ['insert', 'update', 'delete'].includes(log.step) * ), * executionTime: logs.length > 0 ? * logs[logs.length - 1].timestamp.getTime() - logs[0].timestamp.getTime() : 0 * }; * * return report; * } * } */ tracking(): { step: string; details: any; timestamp: Date; }[]; private applyWhereClausesToVirtual; private executeVirtualAction; /** * Define as colunas a serem selecionadas na consulta. * Por padrão seleciona todas as colunas (*) se nenhuma for especificada. * * @param columns - Array de nomes de colunas ou chaves do tipo T * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Seleção de colunas específicas * const users = await new QueryBuilder<User>('users') * .select(['id', 'name', 'email']) * .all(); * * @example * // Exemplo intermediário - Seleção com tipos genéricos * const userStats = await new QueryBuilder<User>('users') * .select(['id', 'name', 'created_at', 'last_login']) * .where('active', '=', true) * .orderBy('created_at', 'DESC') * .all(); * * @example * // Exemplo avançado - Seleção dinâmica baseada em condições * class DynamicSelector { * static getColumnsForRole(role: string): (keyof User)[] { * const baseColumns: (keyof User)[] = ['id', 'name']; * * switch (role) { * case 'admin': * return [...baseColumns, 'email', 'role', 'permissions', 'last_login']; * case 'moderator': * return [...baseColumns, 'email', 'role', 'last_login']; * default: * return [...baseColumns, 'email']; * } * } * * static async getUsersByRole(role: string): Promise<User[]> { * const columns = this.getColumnsForRole(role); * return await new QueryBuilder<User>('users') * .select(columns) * .where('role', '=', role) * .all(); * } * } */ select(columns?: (keyof T | string)[]): this; /** * Adiciona uma expressão SQL raw à seleção. * Permite incluir funções SQL, cálculos ou expressões complexas na consulta. * * @param sql - Expressão SQL raw a ser incluída na seleção * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Expressão SQL simples * const users = await new QueryBuilder<User>('users') * .select(['id', 'name']) * .selectRaw('UPPER(email) as email_upper') * .all(); * * @example * // Exemplo intermediário - Cálculos e funções * const userStats = await new QueryBuilder<User>('users') * .select(['id', 'name']) * .selectRaw('DATEDIFF(NOW(), created_at) as days_since_creation') * .selectRaw('CASE WHEN last_login IS NULL THEN "Never" ELSE "Yes" END as has_logged_in') * .all(); * * @example * // Exemplo avançado - Expressões complexas e subconsultas * const advancedStats = await new QueryBuilder<User>('users') * .select(['id', 'name']) * .selectRaw(` * (SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id) as post_count * `) * .selectRaw(` * (SELECT MAX(created_at) FROM comments WHERE comments.user_id = users.id) as last_comment_date * `) * .selectRaw(` * CASE * WHEN (SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id) > 10 THEN 'Power User' * WHEN (SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id) > 5 THEN 'Active User' * ELSE 'Regular User' * END as user_level * `) * .all(); */ selectRaw(sql: string): this; /** * Adiciona colunas de agregação à seleção. * Útil para incluir múltiplas funções de agregação em uma única consulta. * * @param columns - Array de colunas de agregação a serem incluídas * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Múltiplas agregações * const stats = await new QueryBuilder<User>('users') * .aggregatesSelect(['COUNT(*) as total_users', 'AVG(age) as avg_age']) * .all(); * * @example * // Exemplo intermediário - Agregações com condições * const userMetrics = await new QueryBuilder<User>('users') * .where('active', '=', true) * .aggregatesSelect([ * 'COUNT(*) as active_users', * 'SUM(CASE WHEN last_login > DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as recent_users' * ]) * .all(); * * @example * // Exemplo avançado - Sistema de métricas complexo * class UserAnalytics { * static async getComprehensiveMetrics(): Promise<any> { * return await new QueryBuilder<User>('users') * .aggregatesSelect([ * 'COUNT(*) as total_users', * 'COUNT(CASE WHEN active = 1 THEN 1 END) as active_users', * 'COUNT(CASE WHEN role = "admin" THEN 1 END) as admin_count', * 'AVG(CASE WHEN last_login IS NOT NULL THEN DATEDIFF(NOW(), last_login) END) as avg_days_since_login', * 'MAX(created_at) as newest_user_date', * 'MIN(created_at) as oldest_user_date', * 'SUM(CASE WHEN email_verified = 1 THEN 1 ELSE 0 END) as verified_users', * 'ROUND((COUNT(CASE WHEN email_verified = 1 THEN 1 END) * 100.0 / COUNT(*)), 2) as verification_rate' * ]) * .all(); * } * } */ aggregatesSelect(columns: string[]): this; /** * Aplica DISTINCT à consulta para eliminar registros duplicados. * Útil quando se trabalha com dados que podem conter duplicatas. * * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Eliminar duplicatas simples * const uniqueEmails = await new QueryBuilder<User>('users') * .select(['email']) * .distinct() * .all(); * * @example * // Exemplo intermediário - DISTINCT com múltiplas colunas * const uniqueCombinations = await new QueryBuilder<User>('users') * .select(['city', 'state']) * .distinct() * .where('active', '=', true) * .orderBy('city', 'ASC') * .all(); * * @example * // Exemplo avançado - Sistema de análise de dados únicos * class DataAnalyzer { * static async findDuplicatePatterns(): Promise<any> { * // Primeiro, encontramos todas as combinações únicas * const uniquePatterns = await new QueryBuilder<User>('users') * .select(['email_domain', 'registration_source', 'user_agent']) * .distinct() * .all(); * * // Depois, comparamos com o total para encontrar padrões * const totalCombinations = await new QueryBuilder<User>('users') * .select(['email_domain', 'registration_source', 'user_agent']) * .all(); * * return { * uniquePatterns: uniquePatterns.length, * totalCombinations: totalCombinations.length, * duplicateRate: ((totalCombinations.length - uniquePatterns.length) / totalCombinations.length * 100).toFixed(2) * }; * } * } */ distinct(): this; /** * Adiciona uma cláusula WHERE básica à consulta. * Cria uma condição de filtro usando o operador especificado. * * @param column - Nome da coluna ou chave do tipo T para filtrar * @param operator - Operador de comparação SQL (ex: '=', '>', 'LIKE', etc.) * @param value - Valor para comparação * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Filtro simples por igualdade * const users = await new QueryBuilder<User>('users') * .where('active', '=', true) * .all(); * * @example * // Exemplo intermediário - Múltiplas condições WHERE * const activeAdmins = await new QueryBuilder<User>('users') * .where('active', '=', true) * .where('role', '=', 'admin') * .where('last_login', '>', new Date('2024-01-01')) * .all(); * * @example * // Exemplo avançado - Sistema de filtros dinâmicos * class DynamicFilterBuilder { * static buildUserFilter(filters: UserFilters): QueryBuilder<User> { * let query = new QueryBuilder<User>('users'); * * if (filters.active !== undefined) { * query = query.where('active', '=', filters.active); * } * * if (filters.role) { * query = query.where('role', '=', filters.role); * } * * if (filters.ageRange) { * query = query.where('age', '>=', filters.ageRange.min) * .where('age', '<=', filters.ageRange.max); * } * * if (filters.searchTerm) { * query = query.where('name', 'LIKE', `%${filters.searchTerm}%`); * } * * return query; * } * } */ where(column: keyof T | string, operator: Operator, value: any): this; /** * Adiciona uma cláusula OR WHERE à consulta. * Cria uma condição alternativa que será conectada com OR às condições anteriores. * * @param column - Nome da coluna ou chave do tipo T para filtrar * @param operator - Operador de comparação SQL * @param value - Valor para comparação * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Condição OR simples * const users = await new QueryBuilder<User>('users') * .where('role', '=', 'admin') * .orWhere('role', '=', 'moderator') * .all(); * * @example * // Exemplo intermediário - Múltiplas condições OR * const priorityUsers = await new QueryBuilder<User>('users') * .where('active', '=', true) * .orWhere('role', '=', 'admin') * .orWhere('last_login', '>', new Date('2024-01-01')) * .orWhere('email', 'LIKE', '%@company.com') * .all(); * * @example * // Exemplo avançado - Sistema de permissões complexo * class PermissionChecker { * static async getUsersWithAccess(requiredPermissions: string[]): Promise<User[]> { * let query = new QueryBuilder<User>('users') * .where('active', '=', true); * * // Usuários com qualquer uma das permissões necessárias * requiredPermissions.forEach(permission => { * query = query.orWhere('permissions', 'LIKE', `%${permission}%`); * }); * * // OU usuários com role de super admin * query = query.orWhere('role', '=', 'super_admin'); * * // OU usuários com acesso de emergência * query = query.orWhere('emergency_access', '=', true); * * return await query.all(); * } * } */ orWhere(column: keyof T | string, operator: Operator, value: any): this; /** * Adiciona uma cláusula WHERE condicionalmente. * Só aplica o filtro se a condição for verdadeira (não null, undefined ou string vazia). * * @param condition - Condição que determina se o filtro será aplicado * @param column - Nome da coluna ou chave do tipo T para filtrar * @param operator - Operador de comparação SQL * @param value - Valor para comparação * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Filtro condicional simples * const users = await new QueryBuilder<User>('users') * .whereIf(searchTerm, 'name', 'LIKE', `%${searchTerm}%`) * .all(); * * @example * // Exemplo intermediário - Múltiplos filtros condicionais * const filteredUsers = await new QueryBuilder<User>('users') * .whereIf(filters.role, 'role', '=', filters.role) * .whereIf(filters.city, 'city', '=', filters.city) * .whereIf(filters.minAge, 'age', '>=', filters.minAge) * .whereIf(filters.maxAge, 'age', '<=', filters.maxAge) * .all(); * * @example * // Exemplo avançado - Sistema de filtros inteligente * class SmartFilterBuilder { * static async buildAdvancedSearch(filters: AdvancedFilters): Promise<User[]> { * let query = new QueryBuilder<User>('users'); * * // Filtros básicos sempre aplicados * query = query.where('active', '=', true); * * // Filtros condicionais baseados em dados disponíveis * query = query.whereIf(filters.name, 'name', 'LIKE', `%${filters.name}%`) * .whereIf(filters.email, 'email', 'LIKE', `%${filters.email}%`) * .whereIf(filters.phone, 'phone', 'LIKE', `%${filters.phone}%`); * * // Filtros de data condicionais * if (filters.dateRange) { * query = query.whereIf(filters.dateRange.start, 'created_at', '>=', filters.dateRange.start) * .whereIf(filters.dateRange.end, 'created_at', '<=', filters.dateRange.end); * } * * // Filtros de localização condicionais * if (filters.location) { * query = query.whereIf(filters.location.country, 'country', '=', filters.location.country) * .whereIf(filters.location.state, 'state', '=', filters.location.state) * .whereIf(filters.location.city, 'city', '=', filters.location.city); * } * * return await query.all(); * } * } */ whereIf(condition: any, column: keyof T | string, operator: Operator, value: any): this; /** * Adiciona múltiplas cláusulas WHERE baseadas em um objeto de condições. * Cada propriedade do objeto se torna uma condição WHERE com operador '='. * * @param conditions - Objeto com pares chave-valor para criar condições WHERE * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Múltiplas condições simples * const users = await new QueryBuilder<User>('users') * .whereAll({ active: true, role: 'user' }) * .all(); * * @example * // Exemplo intermediário - Condições com filtros dinâmicos * const filterCriteria = { * active: true, * verified: true, * subscription_status: 'premium' * }; * * const premiumUsers = await new QueryBuilder<User>('users') * .whereAll(filterCriteria) * .orderBy('created_at', 'DESC') * .all(); * * @example * // Exemplo avançado - Sistema de filtros baseado em perfil * class ProfileBasedFilter { * static async getUsersByProfile(profile: UserProfile): Promise<User[]> { * // Filtros baseados no perfil do usuário * const baseFilters: Partial<User> = { * active: true, * verified: true * }; * * // Adiciona filtros específicos baseados no perfil * if (profile.preferences.includeInactive) { * delete baseFilters.active; * } * * if (profile.preferences.includeUnverified) { * delete baseFilters.verified; * } * * // Filtros de localização * if (profile.location) { * Object.assign(baseFilters, { * country: profile.location.country, * timezone: profile.location.timezone * }); * } * * // Filtros de comportamento * if (profile.behavior) { * Object.assign(baseFilters, { * last_activity: profile.behavior.minActivityLevel, * engagement_score: profile.behavior.minEngagementScore * }); * } * * return await new QueryBuilder<User>('users') * .whereAll(baseFilters) * .orderBy('relevance_score', 'DESC') * .limit(profile.preferences.maxResults || 50) * .all(); * } * } */ whereAll(conditions: Partial<T>): this; /** * Prepara uma operação de INSERT na tabela. * Os dados não são inseridos até que .make() seja chamado. * * @param data - Dados a serem inseridos (objeto único ou array de objetos) * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Inserir um usuário * const result = await new QueryBuilder<User>('users') * .insert({ name: 'John Doe', email: 'john@example.com' }) * .make(); * * @example * // Exemplo intermediário - Inserir múltiplos usuários * const users = [ * { name: 'John Doe', email: 'john@example.com', role: 'user' }, * { name: 'Jane Smith', email: 'jane@example.com', role: 'admin' } * ]; * * const result = await new QueryBuilder<User>('users') * .insert(users) * .make(); * * @example * // Exemplo avançado - Sistema de importação em lote com validação * class BatchImporter { * static async importUsersFromCSV(csvData: string[][]): Promise<ImportResult> { * const headers = csvData[0]; * const rows = csvData.slice(1); * * const validUsers: Partial<User>[] = []; * const errors: string[] = []; * * rows.forEach((row, index) => { * try { * const user = this.parseCSVRow(headers, row); * if (this.validateUser(user)) { * validUsers.push(user); * } else { * errors.push(`Row ${index + 2}: Invalid user data`); * } * } catch (error) { * errors.push(`Row ${index + 2}: ${error.message}`); * } * }); * * if (validUsers.length > 0) { * const result = await new QueryBuilder<User>('users') * .insert(validUsers) * .make(); * * return { * success: true, * inserted: result.changes, * errors, * lastInsertId: result.lastInsertRowid * }; * } * * return { success: false, inserted: 0, errors, lastInsertId: 0 }; * } * } */ insert(data: Partial<T> | Partial<T>[]): this; /** * Prepara uma operação de UPDATE na tabela. * Os dados não são atualizados até que .make() seja chamado. * Requer pelo menos uma cláusula WHERE para segurança. * * @param data - Objeto com os campos e valores a serem atualizados * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Atualizar um usuário específico * const result = await new QueryBuilder<User>('users') * .where('id', '=', 1) * .update({ lastLogin: new Date(), loginCount: 5 }) * .make(); * * @example * // Exemplo intermediário - Atualização em lote com condições * const result = await new QueryBuilder<User>('users') * .where('role', '=', 'user') * .where('lastLogin', '<', new Date('2024-01-01')) * .update({ * status: 'inactive', * updatedAt: new Date(), * deactivationReason: 'Inactive for more than 6 months' * }) * .make(); * * @example * // Exemplo avançado - Sistema de auditoria com histórico * class AuditSystem { * static async updateUserWithAudit(userId: number, updates: Partial<User>, auditor: string): Promise<void> { * // Primeiro, registra o estado anterior * const previousState = await new QueryBuilder<User>('users') * .where('id', '=', userId) * .get(); * * if (!previousState) { * throw new Error(`User ${userId} not found`); * } * * // Cria registro de auditoria * await new QueryBuilder<AuditLog>('user_audit_logs') * .insert({ * userId, * action: 'UPDATE', * previousState: JSON.stringify(previousState), * newState: JSON.stringify({ ...previousState, ...updates }), * auditor, * timestamp: new Date(), * ipAddress: this.getClientIP() * }) * .make(); * * // Executa a atualização * const result = await new QueryBuilder<User>('users') * .where('id', '=', userId) * .update({ * ...updates, * updatedAt: new Date(), * updatedBy: auditor * }) * .make(); * * if (result.changes === 0) { * throw new Error('Update failed - no rows affected'); * } * } * } */ update(data: Partial<T>): this; /** * Prepara uma operação de DELETE na tabela. * Os registros não são deletados até que .make() seja chamado. * Requer pelo menos uma cláusula WHERE para segurança. * * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Deletar um usuário específico * const result = await new QueryBuilder<User>('users') * .where('id', '=', 1) * .delete() * .make(); * * @example * // Exemplo intermediário - Deletar usuários inativos * const result = await new QueryBuilder<User>('users') * .where('active', '=', false) * .where('lastLogin', '<', new Date('2020-01-01')) * .delete() * .make(); * * @example * // Exemplo avançado - Sistema de soft delete com backup * class SafeDeleteSystem { * static async safeDeleteUser(userId: number, reason: string): Promise<void> { * // Primeiro, faz backup dos dados * const userData = await new QueryBuilder<User>('users') * .where('id', '=', userId) * .get(); * * if (!userData) { * throw new Error(`User ${userId} not found`); * } * * // Cria backup na tabela de arquivo * await new QueryBuilder<DeletedUser>('deleted_users') * .insert({ * originalId: userId, * userData: JSON.stringify(userData), * deletedAt: new Date(), * deletedBy: this.getCurrentUser(), * deletionReason: reason, * backupLocation: 'deleted_users_table' * }) * .make(); * * // Executa o delete real * const result = await new QueryBuilder<User>('users') * .where('id', '=', userId) * .delete() * .make(); * * // Registra a operação no log de sistema * await new QueryBuilder<SystemLog>('system_logs') * .insert({ * action: 'USER_DELETED', * targetId: userId, * details: `User deleted with reason: ${reason}`, * timestamp: new Date(), * severity: 'INFO' * }) * .make(); * } * } */ delete(): this; /** * Prepara uma operação de UPDATE OR INSERT (upsert). * Tenta atualizar um registro existente, se não encontrar, insere um novo. * * @param attributes - Atributos para identificar o registro (usado no WHERE) * @param values - Valores a serem inseridos/atualizados * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Upsert simples * const result = await new QueryBuilder<User>('users') * .updateOrInsert( * { email: 'john@example.com' }, // atributos para busca * { name: 'John Doe', role: 'user' } // valores para inserir/atualizar * ) * .make(); * * @example * // Exemplo intermediário - Upsert com múltiplos campos de identificação * const result = await new QueryBuilder<Order>('orders') * .updateOrInsert( * { userId: 123, productId: 456 }, // chave composta * { * quantity: 2, * price: 29.99, * updatedAt: new Date() * } * ) * .make(); * * @example * // Exemplo avançado - Sistema de sincronização de dados * class DataSynchronizer { * static async syncUserProfile(externalData: ExternalUserProfile): Promise<SyncResult> { * const attributes = { * externalId: externalData.id, * source: externalData.source * }; * * const values = { * name: externalData.name, * email: externalData.email, * avatar: externalData.avatar, * lastSync: new Date(), * syncVersion: externalData.version, * metadata: JSON.stringify(externalData.metadata) * }; * * // Tenta atualizar ou inserir * const result = await new QueryBuilder<UserProfile>('user_profiles') * .updateOrInsert(attributes, values) * .make(); * * // Se foi uma inserção, cria registros relacionados * if (result.changes > 0 && result.lastInsertRowid) { * await this.createRelatedRecords(result.lastInsertRowid, externalData); * } * * return { * success: true, * action: result.changes > 0 ? 'INSERTED' : 'UPDATED', * recordId: result.lastInsertRowid, * timestamp: new Date() * }; * } * } */ updateOrInsert(attributes: Partial<T>, values?: Partial<T>): this; /** * Prepara uma operação de incremento de campo numérico. * Adiciona o valor especificado ao campo atual. * * @param column - Nome da coluna numérica a ser incrementada * @param amount - Quantidade a ser adicionada (padrão: 1) * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Incrementar contador de login * const result = await new QueryBuilder<User>('users') * .where('id', '=', 1) * .increment('loginCount') * .make(); * * @example * // Exemplo intermediário - Incrementar com valor customizado * const result = await new QueryBuilder<Inventory>('inventory') * .where('productId', '=', 123) * .increment('stock', 10) * .make(); * * @example * // Exemplo avançado - Sistema de pontos e gamificação * class GamificationSystem { * static async awardPoints(userId: number, action: string, points: number): Promise<void> { * // Incrementa pontos do usuário * const result = await new QueryBuilder<User>('users') * .where('id', '=', userId) * .increment('points', points) * .make(); * * // Registra a ação para histórico * await new QueryBuilder<PointHistory>('point_history') * .insert({ * userId, * action, * points, * timestamp: new Date(), * balance: await this.getCurrentBalance(userId) * }) * .make(); * * // Verifica se atingiu novo nível * await this.checkLevelUpgrade(userId); * * // Notifica outros sistemas * await this.notifyAchievement(userId, action, points); * } * } */ increment(column: keyof T, amount?: number): this; /** * Prepara uma operação de decremento de campo numérico. * Subtrai o valor especificado do campo atual. * * @param column - Nome da coluna numérica a ser decrementada * @param amount - Quantidade a ser subtraída (padrão: 1) * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Decrementar estoque * const result = await new QueryBuilder<Product>('products') * .where('id', '=', 123) * .decrement('stock') * .make(); * * @example * // Exemplo intermediário - Decrementar com validação * const product = await new QueryBuilder<Product>('products') * .where('id', '=', 123) * .get(); * * if (product.stock >= 5) { * const result = await new QueryBuilder<Product>('products') * .where('id', '=', 123) * .decrement('stock', 5) * .make(); * } * * @example * // Exemplo avançado - Sistema de reservas com controle de concorrência * class ReservationSystem { * static async reserveSeat(eventId: number, userId: number, seatNumber: string): Promise<ReservationResult> { * // Verifica disponibilidade * const seat = await new QueryBuilder<Seat>('seats') * .where('eventId', '=', eventId) * .where('seatNumber', '=', seatNumber) * .where('status', '=', 'available') * .get(); * * if (!seat) { * throw new Error('Seat not available'); * } * * // Decrementa capacidade disponível do evento * const capacityResult = await new QueryBuilder<Event>('events') * .where('id', '=', eventId) * .decrement('availableCapacity') * .make(); * * if (capacityResult.changes === 0) { * throw new Error('Event is full'); * } * * // Marca assento como reservado * await new QueryBuilder<Seat>('seats') * .where('id', '=', seat.id) * .update({ * status: 'reserved', * reservedBy: userId, * reservedAt: new Date() * }) * .make(); * * // Cria a reserva * const reservation = await new QueryBuilder<Reservation>('reservations') * .insert({ * eventId, * userId, * seatId: seat.id, * status: 'confirmed', * createdAt: new Date() * }) * .make(); * * return { * success: true, * reservationId: reservation.lastInsertRowid, * seatNumber, * eventId * }; * } * } */ decrement(column: keyof T, amount?: number): this; /** * Adiciona uma cláusula WHERE IN para verificar se um valor está em uma lista. * Útil para filtrar por múltiplos valores possíveis. * * @param column - Nome da coluna ou chave do tipo T para filtrar * @param values - Array de valores para verificar * @param logical - Conectivo lógico ('AND' ou 'OR', padrão: 'AND') * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Filtro IN simples * const users = await new QueryBuilder<User>('users') * .whereIn('role', ['admin', 'moderator', 'user']) * .all(); * * @example * // Exemplo intermediário - Múltiplos filtros IN * const users = await new QueryBuilder<User>('users') * .whereIn('role', ['admin', 'moderator']) * .whereIn('status', ['active', 'pending']) * .whereIn('department', ['IT', 'HR', 'Sales']) * .all(); * * @example * // Exemplo avançado - Sistema de filtros por categorias * class CategoryFilterSystem { * static async getUsersByCategories(categories: string[], userPreferences: UserPreferences): Promise<User[]> { * let query = new QueryBuilder<User>('users') * .where('active', '=', true); * * // Filtra por categorias de interesse * if (categories.length > 0) { * query = query.whereIn('interest_category', categories); * } * * // Filtra por níveis de acesso baseados nas preferências * const accessLevels = this.calculateAccessLevels(userPreferences); * query = query.whereIn('access_level', accessLevels); * * // Filtra por localizações disponíveis * if (userPreferences.locationRestrictions) { * query = query.whereIn('country', userPreferences.locationRestrictions.allowedCountries); * } * * // Filtra por horários de disponibilidade * const availableTimeSlots = this.getAvailableTimeSlots(userPreferences.timezone); * query = query.whereIn('preferred_time_slot', availableTimeSlots); * * return await query * .orderBy('relevance_score', 'DESC') * .limit(userPreferences.maxResults || 100) * .all(); * } * } */ whereIn(column: keyof T | string, values: any[], logical?: 'AND' | 'OR'): this; /** * Adiciona uma cláusula OR WHERE IN para verificar se um valor está em uma lista. * Conecta com OR às condições anteriores. * * @param column - Nome da coluna ou chave do tipo T para filtrar * @param values - Array de valores para verificar * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Condição OR IN simples * const users = await new QueryBuilder<User>('users') * .where('active', '=', true) * .orWhereIn('role', ['admin', 'super_admin']) * .all(); * * @example * // Exemplo intermediário - Múltiplas condições OR IN * const priorityUsers = await new QueryBuilder<User>('users') * .where('verified', '=', true) * .orWhereIn('role', ['admin', 'moderator']) * .orWhereIn('subscription_type', ['premium', 'enterprise']) * .orWhereIn('last_activity', ['today', 'yesterday']) * .all(); * * @example * // Exemplo avançado - Sistema de permissões hierárquicas * class HierarchicalPermissionSystem { * static async getUsersWithAccess(requiredPermissions: string[], context: PermissionContext): Promise<User[]> { * let query = new QueryBuilder<User>('users') * .where('active', '=', true); * * // Usuários com permissões diretas * query = query.orWhereIn('direct_permissions', requiredPermissions); * * // Usuários com roles que têm as permissões * const rolesWithPermissions = await this.getRolesWithPermissions(requiredPermissions); * query = query.orWhereIn('role', rolesWithPermissions); * * // Usuários em grupos com as permissões * const groupsWithPermissions = await this.getGroupsWithPermissions(requiredPermissions); * query = query.orWhereIn('group_id', groupsWithPermissions); * * // Usuários com permissões herdadas de hierarquia * if (context.includeInherited) { * const inheritedPermissions = await this.getInheritedPermissions(context.userId); * query = query.orWhereIn('inherited_permissions', inheritedPermissions); * } * * // Usuários com permissões temporárias * if (context.includeTemporary) { * const tempPermissions = await this.getTemporaryPermissions(requiredPermissions); * query = query.orWhereIn('temp_permission_id', tempPermissions); * } * * return await query * .orderBy('permission_level', 'DESC') * .limit(context.maxResults || 50) * .all(); * } * } */ orWhereIn(column: keyof T | string, values: any[]): this; /** * Adiciona uma cláusula WHERE NOT IN para verificar se um valor NÃO está em uma lista. * Exclui registros que correspondem aos valores especificados. * * @param column - Nome da coluna ou chave do tipo T para filtrar * @param values - Array de valores para excluir * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Exclusão NOT IN simples * const users = await new QueryBuilder<User>('users') * .whereNotIn('role', ['admin', 'moderator']) * .all(); * * @example * // Exemplo intermediário - Múltiplas exclusões * const regularUsers = await new QueryBuilder<User>('users') * .where('active', '=', true) * .whereNotIn('role', ['admin', 'moderator', 'super_user']) * .whereNotIn('status', ['banned', 'suspended']) * .whereNotIn('email_domain', ['@test.com', '@example.com']) * .all(); * * @example * // Exemplo avançado - Sistema de filtros de segurança * class SecurityFilterSystem { * static async getSecureUserList(excludePatterns: SecurityPatterns): Promise<User[]> { * let query = new QueryBuilder<User>('users') * .where('active', '=', true); * * // Exclui usuários com roles sensíveis * if (excludePatterns.sensitiveRoles) { * query = query.whereNotIn('role', excludePatterns.sensitiveRoles); * } * * // Exclui usuários de domínios suspeitos * if (excludePatterns.suspiciousDomains) { * query = query.whereNotIn('email_domain', excludePatterns.suspiciousDomains); * } * * // Exclui usuários com padrões de comportamento suspeito * if (excludePatterns.suspiciousBehaviors) { * query = query.whereNotIn('behavior_pattern', excludePatterns.suspiciousBehaviors); * } * * // Exclui usuários de localizações restritas * if (excludePatterns.restrictedLocations) { * query = query.whereNotIn('country', excludePatterns.restrictedLocations); * } * * // Exclui usuários com histórico de violações * if (excludePatterns.violationHistory) { * const usersWithViolations = await this.getUsersWithViolations(excludePatterns.violationHistory); * query = query.whereNotIn('id', usersWithViolations); * } * * return await query * .orderBy('security_score', 'ASC') * .limit(excludePatterns.maxResults || 100) * .all(); * } * } */ whereNotIn(column: keyof T | string, values: any[]): this; /** * Adiciona uma cláusula OR WHERE NOT IN para verificar se um valor NÃO está em uma lista. * Conecta com OR às condições anteriores. * * @param column - Nome da coluna ou chave do tipo T para filtrar * @param values - Array de valores para excluir * @returns Instância atual do QueryBuilder para method chaining * * @example * // Exemplo básico - Condição OR NOT IN simples * const users = await new QueryBuilder<User>('users') * .where('active', '=', true) * .orWhereNotIn('role', ['admin', 'moderator']) * .all(); * * @example * // Exemplo intermediário - Múltiplas condições OR NOT IN * const specialUsers = await new QueryBuilder<User>('users') * .where('verified', '=', true) * .orWhereNotIn('subscription_type', ['free', 'basic']) * .orWhereNotIn('last_activity', ['never', 'unknown']) * .orWhereNotIn('email_domain', ['@temp.com', '@disposable.com']) * .all(); * * @example * // Exemplo avançado - Sistema de análise de risco * class RiskAnalysisSystem { * static async getLowRiskUsers(riskFactors: RiskFactors): Promise<User[]> { * let query = new QueryBuilder<User>('users') * .where('active', '=', true); * * // Usuários com baixo risco baseado em múltiplos critérios * query = query.orWhereNotIn('risk_level', ['high', 'critical']) * .orWhereNotIn('suspicious_activity', ['yes', 'confirmed']) * .orWhereNotIn('fraud_score', [8, 9, 10]) * .orWhereNotIn('verification_status', ['pending', 'failed']) * .orWhereNotIn('compliance_status', ['non_compliant', 'under_review']); * * // Exclui usuários com histórico de problemas * const problematicUsers = await this.getProblematicUsers(riskFactors); * query = query.whereNotIn('id', problematicUsers); * * // Exclui usuários de regiões de alto risco * const highRiskRegion