clickhouse-sql-builder
Version:
A minimal, type-safe SQL builder for ClickHouse in the JavaScript ecosystem.
116 lines (115 loc) • 3.63 kB
JavaScript
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;
}
}