UNPKG

quicklite

Version:

A lightweight ORM toolkit for SQLite in Node.js applications

293 lines 9.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.QueryBuilder = void 0; /** * SQL query builder for constructing complex queries */ class QueryBuilder { /** * Creates a new QueryBuilder instance * @param db Database instance * @param tableName Table name */ constructor(db, tableName) { this.selectFields = ['*']; this.whereConditions = []; this.orConditions = []; this.joinConditions = []; this.groupByFields = []; this.havingConditions = []; this.orderByConditions = []; this.params = []; this.db = db; this.tableName = tableName; } /** * Sets the fields to select * @param fields Field names * @returns this instance for chaining */ select(...fields) { if (fields.length > 0) { this.selectFields = fields; } return this; } /** * Adds a WHERE condition * @param field Field name * @param operator Comparison operator * @param value Comparison value * @returns this instance for chaining */ where(field, operator, value) { this.whereConditions.push({ field, operator, value }); if (value !== undefined && !['IS NULL', 'IS NOT NULL'].includes(operator)) { if (operator === 'BETWEEN' && Array.isArray(value)) { this.params.push(value[0], value[1]); } else if (operator === 'IN' || operator === 'NOT IN') { if (Array.isArray(value)) { this.params.push(...value); } } else { this.params.push(value); } } return this; } /** * Adds an AND condition * @param field Field name * @param operator Comparison operator * @param value Comparison value * @returns this instance for chaining */ and(field, operator, value) { return this.where(field, operator, value); } /** * Adds an OR condition * @param conditions Callback for creating OR conditions * @returns this instance for chaining */ or(conditions) { const subQuery = new QueryBuilder(this.db, this.tableName); conditions(subQuery); if (subQuery.whereConditions.length > 0) { this.orConditions.push(subQuery.whereConditions); this.params.push(...subQuery.params); } return this; } /** * Adds a grouped AND condition with parentheses * @param conditions Callback for creating grouped conditions * @returns this instance for chaining */ andWhere(conditions) { const subQuery = new QueryBuilder(this.db, this.tableName); conditions(subQuery); if (subQuery.whereConditions.length > 0) { this.whereConditions.push({ field: `(${this.buildWhereConditionString(subQuery.whereConditions)})`, operator: '=', value: undefined }); this.params.push(...subQuery.params); } return this; } /** * Adds a table join * @param table Table to join * @param type Join type * @param on ON clause * @returns this instance for chaining */ join(table, type, on) { this.joinConditions.push({ table, type, on }); return this; } /** * Adds an INNER JOIN * @param table Table to join * @param on ON clause * @returns this instance for chaining */ innerJoin(table, on) { return this.join(table, 'INNER', on); } /** * Adds a LEFT JOIN * @param table Table to join * @param on ON clause * @returns this instance for chaining */ leftJoin(table, on) { return this.join(table, 'LEFT', on); } /** * Sets GROUP BY fields * @param fields Field names * @returns this instance for chaining */ groupBy(...fields) { this.groupByFields = fields; return this; } /** * Adds a HAVING condition * @param field Field name * @param operator Comparison operator * @param value Comparison value * @returns this instance for chaining */ having(field, operator, value) { this.havingConditions.push({ field, operator, value }); if (value !== undefined && !['IS NULL', 'IS NOT NULL'].includes(operator)) { this.params.push(value); } return this; } /** * Sets ORDER BY clause * @param field Field name * @param direction Sort direction * @returns this instance for chaining */ orderBy(field, direction = 'ASC') { this.orderByConditions.push({ field, direction }); return this; } /** * Sets LIMIT clause * @param limit Maximum number of results * @returns this instance for chaining */ limit(limit) { this.limitValue = limit; return this; } /** * Sets OFFSET clause * @param offset Number of results to skip * @returns this instance for chaining */ offset(offset) { this.offsetValue = offset; return this; } /** * Builds a WHERE condition string from conditions * @param conditions WHERE conditions * @returns WHERE condition string */ buildWhereConditionString(conditions) { return conditions.map(condition => { if (['IS NULL', 'IS NOT NULL'].includes(condition.operator)) { return `${condition.field} ${condition.operator}`; } else if (condition.operator === 'BETWEEN' && Array.isArray(condition.value)) { return `${condition.field} BETWEEN ? AND ?`; } else if (['IN', 'NOT IN'].includes(condition.operator) && Array.isArray(condition.value)) { const placeholders = condition.value.map(() => '?').join(', '); return `${condition.field} ${condition.operator} (${placeholders})`; } else if (condition.field.startsWith('(') && condition.field.endsWith(')')) { // For subqueries, just use the field name (which contains the entire condition) return condition.field; } else { return `${condition.field} ${condition.operator} ?`; } }).join(' AND '); } /** * Builds the complete SQL query * @returns SQL query and parameters */ build() { let sql = `SELECT ${this.selectFields.join(', ')} FROM ${this.tableName}`; const params = [...this.params]; // Add JOINs if (this.joinConditions.length > 0) { const joinStr = this.joinConditions.map(join => `${join.type} JOIN ${join.table} ON ${join.on}`).join(' '); sql += ` ${joinStr}`; } // Add WHERE conditions const whereConditions = []; if (this.whereConditions.length > 0) { whereConditions.push(this.buildWhereConditionString(this.whereConditions)); } // Add OR conditions if (this.orConditions.length > 0) { const orClauses = this.orConditions.map(conditions => `(${this.buildWhereConditionString(conditions)})`); if (orClauses.length > 0) { whereConditions.push(`(${orClauses.join(' OR ')})`); } } if (whereConditions.length > 0) { sql += ` WHERE ${whereConditions.join(' AND ')}`; } // Add GROUP BY if (this.groupByFields.length > 0) { sql += ` GROUP BY ${this.groupByFields.join(', ')}`; } // Add HAVING if (this.havingConditions.length > 0) { sql += ` HAVING ${this.buildWhereConditionString(this.havingConditions)}`; } // Add ORDER BY if (this.orderByConditions.length > 0) { const orderStr = this.orderByConditions.map(order => `${order.field} ${order.direction}`).join(', '); sql += ` ORDER BY ${orderStr}`; } // Add LIMIT and OFFSET if (this.limitValue !== undefined) { sql += ` LIMIT ${this.limitValue}`; if (this.offsetValue !== undefined) { sql += ` OFFSET ${this.offsetValue}`; } } return { sql, params }; } /** * Executes the query and returns all results * @returns Query results */ all() { const { sql, params } = this.build(); const stmt = this.db.prepare(sql); return stmt.all(...params); } /** * Executes the query and returns the first result * @returns First result or null if none */ first() { const { sql, params } = this.build(); const stmt = this.db.prepare(sql); return stmt.get(...params) || null; } /** * Counts the number of matching records * @returns Record count */ count() { // Save the current select fields const originalSelectFields = [...this.selectFields]; // Change select to COUNT(*) this.selectFields = ['COUNT(*) as count']; const { sql, params } = this.build(); const stmt = this.db.prepare(sql); const result = stmt.get(...params); // Restore original select fields this.selectFields = originalSelectFields; return (result === null || result === void 0 ? void 0 : result.count) || 0; } } exports.QueryBuilder = QueryBuilder; //# sourceMappingURL=QueryBuilder.js.map