iagate-querykit
Version:
QueryKit: lightweight TypeScript query toolkit with models, views, triggers, events, scheduler and adapters (better-sqlite3).
1,319 lines • 333 kB
TypeScript
/**
* 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