UNPKG

@timescaledb/core

Version:

The `@timescaledb/core` package provides fundamental building blocks for working with TimescaleDB in TypeScript/JavaScript applications. It includes SQL query builders and utilities for managing hypertables, continuous aggregates, compression, and other T

181 lines 7.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ContinuousAggregate = void 0; const schemas_1 = require("@timescaledb/schemas"); const utils_1 = require("@timescaledb/utils"); const debug_1 = require("./debug"); const debug = (0, debug_1.debugCore)('ContinuousAggregate'); class ContinuousAggregateInspectBuilder { constructor(name) { this.name = name; } build() { debug(`Building inspect query for continuous aggregate '${this.name}'`); const literalName = (0, utils_1.escapeLiteral)(this.name); const result = `SELECT EXISTS ( SELECT FROM timescaledb_information.continuous_aggregates WHERE view_name = ${literalName} ) as hypertable_exists;`; debug(`Inspect query built for '${this.name}':\n${result}`); return result; } } class ContinuousAggregateUpBuilder { constructor(name, source, options) { this.name = name; this.source = source; this.options = options; } generateAggregate(config) { const alias = (0, utils_1.escapeIdentifier)(config.column_alias); switch (config.type) { case 'count': return `COUNT(*) as ${alias}`; case 'count_distinct': { if (!config.column) { throw new Error('Column is required for count_distinct aggregate'); } const column = (0, utils_1.escapeIdentifier)(config.column); return `COUNT(DISTINCT ${column}) as ${alias}`; } case 'sum': { if (!config.column) { throw new Error('Column is required for sum aggregate'); } const sumColumn = (0, utils_1.escapeIdentifier)(config.column); return `SUM(${sumColumn}) as ${alias}`; } case 'avg': { if (!config.column) { throw new Error('Column is required for avg aggregate'); } const avgColumn = (0, utils_1.escapeIdentifier)(config.column); return `AVG(${avgColumn}) as ${alias}`; } case 'min': { if (!config.column) { throw new Error('Column is required for min aggregate'); } const minColumn = (0, utils_1.escapeIdentifier)(config.column); return `MIN(${minColumn}) as ${alias}`; } case 'max': { if (!config.column) { throw new Error('Column is required for max aggregate'); } const maxColumn = (0, utils_1.escapeIdentifier)(config.column); return `MAX(${maxColumn}) as ${alias}`; } case 'bucket': { if (!config.column) { throw new Error('Column is required for bucket aggregate'); } const interval = (0, utils_1.escapeLiteral)(this.options.bucket_interval); const bucketColumn = (0, utils_1.escapeIdentifier)(config.column); return `time_bucket(${interval}, ${bucketColumn}) as ${alias}`; } case 'candlestick': { if (!config.time_column || !config.price_column) { throw new Error('time_column and price_column are required for candlestick aggregate'); } const args = [config.time_column, config.price_column]; if (config.volume_column) { args.push(config.volume_column); } return `candlestick_agg(${args.map(utils_1.escapeIdentifier).join(', ')}) as ${alias}`; } default: throw new Error(`Unsupported aggregate type: ${config.type}`); } } generateSelect() { const sourceName = (0, utils_1.escapeIdentifier)(this.source); const hasGroupByColumns = []; const aggregates = []; const bucketAggregate = Object.entries(this.options.aggregates || []).find(([, config]) => config.type === schemas_1.AggregateType.Bucket); const bucketColumnAlias = (0, utils_1.escapeIdentifier)(bucketAggregate?.[1].column_alias || 'bucket'); const generatedBucketStr = bucketAggregate ? this.generateAggregate(bucketAggregate[1]) : this.generateAggregate({ type: schemas_1.AggregateType.Bucket, column: this.options.time_column, column_alias: 'bucket', }); aggregates.push(generatedBucketStr); hasGroupByColumns.push(bucketColumnAlias); const primaryColumns = this.options.group_columns || []; primaryColumns.forEach((column) => { const columnName = (0, utils_1.escapeIdentifier)(column); aggregates.push(`${columnName} as ${columnName}`); hasGroupByColumns.push(columnName); }); Object.entries(this.options.aggregates || []).forEach(([, config]) => { if (config.type === schemas_1.AggregateType.Bucket) return; aggregates.push(this.generateAggregate(config)); }); const result = ` SELECT ${aggregates.join(',\n ')} FROM ${sourceName} GROUP BY ${hasGroupByColumns.join(', ')} `; return result; } getRefreshPolicy() { if (!this.options.refresh_policy) return null; debug(`Building refresh policy for continuous aggregate '${this.name}'`); const policy = this.options.refresh_policy; const viewName = (0, utils_1.escapeLiteral)(this.name); const result = `SELECT add_continuous_aggregate_policy(${viewName}, start_offset => INTERVAL ${(0, utils_1.escapeLiteral)(policy.start_offset)}, end_offset => INTERVAL ${(0, utils_1.escapeLiteral)(policy.end_offset)}, schedule_interval => INTERVAL ${(0, utils_1.escapeLiteral)(policy.schedule_interval)} );`; debug(`Refresh policy built for '${this.name}':\n${result}`); return result; } build() { debug(`Building up query for continuous aggregate '${this.name}'`); const viewName = (0, utils_1.escapeIdentifier)(this.name); const result = `CREATE MATERIALIZED VIEW ${viewName} WITH (timescaledb.continuous) AS ${this.generateSelect()} WITH NO DATA;`; debug(`Up query built for '${this.name}':\n${result}`); return result; } } class ContinuousAggregateDownBuilder { constructor(name, options) { this.name = name; this.options = options; } build() { debug(`Building down query for continuous aggregate '${this.name}'`); const statements = []; const viewName = this.name; if (this.options.refresh_policy) { statements.push(`SELECT remove_continuous_aggregate_policy(${(0, utils_1.escapeLiteral)(viewName)}, if_exists => true);`); } statements.push(`DROP MATERIALIZED VIEW IF EXISTS ${(0, utils_1.escapeIdentifier)(viewName)};`); debug(`Down query built for '${this.name}':\n${statements.join('\n')}`); return statements; } } class ContinuousAggregate { constructor(name, source, options) { this.name = name; this.source = source; this.options = schemas_1.CreateContinuousAggregateOptionsSchema.parse(options); } up() { return new ContinuousAggregateUpBuilder(this.name, this.source, this.options); } down() { return new ContinuousAggregateDownBuilder(this.name, this.options); } inspect() { return new ContinuousAggregateInspectBuilder(this.name); } } exports.ContinuousAggregate = ContinuousAggregate; //# sourceMappingURL=continuous-aggregate.js.map