import {EscapedValue, isWhereClause, isWhereGroup, WhereStatement} from '../types.ts' import {escape, SQLWhereOperator, WherePreOperator} from '../types.ts' import {WhereBuilderFunction} from './Select.ts' import {apply_filter_to_where, QueryFilter} from '../../model/filter.ts' import {Scope} from '../Scope.ts' import {FunctionScope, ScopeFunction} from '../scope/FunctionScope.ts' import {make} from '../../../../di/src/global.ts' export class WhereBuilder { protected _wheres: WhereStatement[] = [] protected _scopes: Scope[] = [] get where_items() { return this._wheres } without_scope(scope: typeof Scope) { this._scopes = this._scopes.filter(x => !(x instanceof Scope)) return this } with_scope(scope: Scope | ScopeFunction) { if ( scope instanceof Scope ) { this._scopes.push(scope) } else { this._scopes.push(new FunctionScope(scope)) } return this } with_scopes(scopes: (Scope | ScopeFunction)[]) { scopes.forEach(scope => this.with_scope(scope)) return this } wheres_to_sql(wheres?: WhereStatement[], level = 0): string { this._scopes.forEach(scope => scope.apply(this)) const indent = Array(level * 2).fill(' ').join('') let statements = [] for ( const where of wheres || this._wheres ) { if ( isWhereClause(where) ) { statements.push(`${indent}${statements.length < 1 ? '' : where.preop + ' '}${where.field} ${where.operator} ${where.operand}`) } else if ( isWhereGroup(where) ) { statements.push(`${indent}${statements.length < 1 ? '' : where.preop + ' '}(\n${this.wheres_to_sql(where.items, level + 1)}\n${indent})`) } } return statements.filter(Boolean).join('\n') } private _createWhere(preop: WherePreOperator, field: string | WhereBuilderFunction, operator?: SQLWhereOperator, operand?: any) { if ( typeof field === 'function' ) { const where_builder = new WhereBuilder() field(where_builder) this._wheres.push({ preop, items: where_builder.where_items, }) } else if ( field && operator && typeof operand !== 'undefined' ) { this._wheres.push({ field, operator, operand: escape(operand), preop }) } } where(field: string | WhereBuilderFunction, operator?: SQLWhereOperator, operand?: any) { this._createWhere('AND', field, operator, operand) return this } whereIn(field: string, values: EscapedValue) { this._wheres.push({ field, operator: 'IN', operand: escape(values), preop: 'AND', }) return this } whereNot(field: string | WhereBuilderFunction, operator?: SQLWhereOperator, operand?: EscapedValue) { this._createWhere('AND NOT', field, operator, operand) return this } whereNotIn(field: string, values: EscapedValue) { this._wheres.push({ field, operator: 'NOT IN', operand: escape(values), preop: 'AND' }) return this } orWhere(field: string | WhereBuilderFunction, operator?: SQLWhereOperator, operand?: EscapedValue) { this._createWhere('OR', field, operator, operand) return this } orWhereNot(field: string | WhereBuilderFunction, operator?: SQLWhereOperator, operand?: EscapedValue) { this._createWhere('OR NOT', field, operator, operand) return this } orWhereIn(field: string, values: EscapedValue) { this._wheres.push({ field, operator: 'IN', operand: escape(values), preop: 'OR', }) return this } orWhereNotIn(field: string, values: EscapedValue) { this._wheres.push({ field, operator: 'NOT IN', operand: escape(values), preop: 'OR', }) return this } whereBetween(field: string, lower_bound: EscapedValue, upper_bound: EscapedValue) { this._wheres.push({ field, operator: 'BETWEEN', operand: `${escape(lower_bound)} AND ${escape(upper_bound)}`, preop: 'AND', }) return this } orWhereBetween(field: string, lower_bound: EscapedValue, upper_bound: EscapedValue) { this._wheres.push({ field, operator: 'BETWEEN', operand: `${escape(lower_bound)} AND ${escape(upper_bound)}`, preop: 'OR', }) return this } whereNotBetween(field: string, lower_bound: EscapedValue, upper_bound: EscapedValue) { this._wheres.push({ field, operator: 'NOT BETWEEN', operand: `${escape(lower_bound)} AND ${escape(upper_bound)}`, preop: 'AND', }) return this } orWhereNotBetween(field: string, lower_bound: EscapedValue, upper_bound: EscapedValue) { this._wheres.push({ field, operator: 'NOT BETWEEN', operand: `${escape(lower_bound)} AND ${escape(upper_bound)}`, preop: 'OR', }) return this } filter(filter: QueryFilter) { return apply_filter_to_where(filter, this) } }