@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
JavaScript
;
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