UNPKG

clickhouse-sql-builder

Version:

A minimal, type-safe SQL builder for ClickHouse in the JavaScript ecosystem.

116 lines (115 loc) 3.63 kB
const clickHouseParamTypes = [ 'JSON', 'Dynamic', 'Object', 'Polygon', 'Ring', 'Point', 'SimpleAggregateFunction', 'IntervalYear', 'IntervalMonth', 'IntervalWeek', 'IntervalDay', 'Enum', 'IntervalMillisecond', 'Int64', 'IntervalMicrosecond', 'MultiLineString', 'IntervalNanosecond', 'Nested', 'LowCardinality', 'Decimal', 'AggregateFunction', 'Float64', 'IntervalHour', 'IPv6', 'Variant', 'IPv4', 'UUID', 'Nothing', 'UInt32', 'DateTime', 'Nullable', 'Tuple', 'IntervalMinute', 'FixedString', 'String', 'DateTime64', 'IntervalQuarter', 'Date', 'Int16', 'Decimal128', 'Array', 'DateTime32', 'Date32', 'UInt256', 'Int8', 'Decimal64', 'Int256', 'Bool', 'Int128', 'Enum8', 'UInt64', 'Float32', 'BFloat16', 'Enum16', 'MultiPolygon', 'LineString', 'UInt128', 'UInt16', 'Map', 'UInt8', 'Decimal256', 'IntervalSecond', 'Int32', 'Decimal32' ]; export class ClickHouseQueryBuilder { sql = ''; fromTable = ''; selectedFields = []; conditions = []; _groupBy = ''; _orderBy = ''; _withFill = ''; _limit; _params = []; static select(fields) { const builder = new ClickHouseQueryBuilder(); builder.selectedFields = fields; return builder; } from(table) { this.fromTable = table; return this; } where(condition) { this.conditions.push(condition); return this; } whereBetween(field, start, end) { this.conditions.push(`${field} BETWEEN ${start} AND ${end}`); return this; } andBetween(field, start, end) { return this.whereBetween(field, start, end); } and(condition) { this.conditions.push(condition); return this; } groupBy(field) { if (typeof field === 'string') this._groupBy = `GROUP BY ${field}`; else this._groupBy = `GROUP BY ${field.join(',')}`; return this; } orderBy(field, order) { this._orderBy = `ORDER BY ${field} ${order ?? ''}`; return this; } withFill({ from, to, step }) { this._withFill = `WITH FILL FROM ${from} TO ${to} STEP ${step}`; return this; } limit(limit) { this._limit = limit; return this; } addParam(type, param) { for (const [key, value] of Object.entries(param)) { this._params.push([key, value, type]); } return this; } queryParams() { return this._params.reduce((acc, [key, value, type]) => { acc[key] = value; return acc; }, {}); } paramTypes() { return this._params.reduce((acc, [key, _value, type]) => { acc[key] = type; return acc; }, {}); } toSql() { const paramTypes = this.paramTypes(); const sql = [ `SELECT ${this.selectedFields.join(', ')}`, `FROM ${this.fromTable}`, `WHERE ${this.conditions.join(' AND ')}`, this._groupBy, this._orderBy, this._withFill, Number.isInteger(this._limit) ? `LIMIT ${this._limit}` : '' ] .filter(val => !!val) .join(' \n\t'); return replaceFields(sql, paramTypes); } toString() { return this.toSql(); } } export default {}; function replaceFields(sql, params) { if (sql.indexOf(':') > -1) { const keys = Object.keys(params); for (const key of keys) { sql = sql.replaceAll(`:${key}`, `{${key}: ${params[key]}}`); } return sql; } else { return sql; } }