drizzle-cube
Version:
Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.
1,241 lines (1,172 loc) • 44.2 kB
TypeScript
import { AnyColumn } from 'drizzle-orm';
import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
import { MySql2Database } from 'drizzle-orm/mysql2';
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import { SQL } from 'drizzle-orm';
import { Subquery } from 'drizzle-orm';
import { Table } from 'drizzle-orm';
import { View } from 'drizzle-orm';
/**
* Abstract base class for database executors
*/
export declare abstract class BaseDatabaseExecutor implements DatabaseExecutor {
db: DrizzleDatabase;
schema?: any | undefined;
databaseAdapter: DatabaseAdapter;
constructor(db: DrizzleDatabase, schema?: any | undefined, engineType?: 'postgres' | 'mysql' | 'sqlite' | 'singlestore');
abstract execute<T = any[]>(query: SQL | any, numericFields?: string[]): Promise<T>;
abstract getEngineType(): 'postgres' | 'mysql' | 'sqlite' | 'singlestore';
}
/**
* Base query definition that can be extended dynamically
* Returns just the FROM/JOIN/WHERE setup, not a complete SELECT
*/
export declare interface BaseQueryDefinition {
/** Main table to query from */
from: QueryableRelation;
/** Optional joins to other tables */
joins?: Array<{
table: QueryableRelation;
on: SQL;
type?: 'left' | 'right' | 'inner' | 'full';
}>;
/** Base WHERE conditions (typically security context filtering) */
where?: SQL;
}
/**
* Compiled cube with execution function
*/
export declare interface CompiledCube extends Cube {
/** Execute a query against this cube */
queryFn: (query: SemanticQuery, securityContext: SecurityContext) => Promise<QueryResult>;
}
/**
* Auto-detect database type and create appropriate executor
* @param db - Drizzle database instance
* @param schema - Optional schema for type inference
* @param engineType - Optional explicit engine type override
* @returns Appropriate database executor
*/
export declare function createDatabaseExecutor(db: DrizzleDatabase, schema?: any, engineType?: 'postgres' | 'mysql' | 'sqlite' | 'singlestore'): DatabaseExecutor;
/**
* Create a new semantic layer instance with Drizzle integration
* Use this when you need multiple isolated instances
*/
export declare function createDrizzleSemanticLayer(options: {
drizzle: DrizzleDatabase;
schema?: any;
}): SemanticLayerCompiler;
/**
* Helper to create multi-cube query context
*/
export declare function createMultiCubeContext(baseContext: QueryContext, cubes: Map<string, Cube>, currentCube: Cube): MultiCubeQueryContext;
/**
* Factory function for creating MySQL executors
*/
export declare function createMySQLExecutor(db: DrizzleDatabase, schema?: any): MySQLExecutor;
/**
* Factory function for creating PostgreSQL executors
*/
export declare function createPostgresExecutor(db: DrizzleDatabase, schema?: any): PostgresExecutor;
/**
* Create a new semantic layer compiler with type inference
*/
export declare function createSemanticLayer(options?: {
drizzle?: DatabaseExecutor['db'];
schema?: any;
databaseExecutor?: DatabaseExecutor;
engineType?: 'postgres' | 'mysql' | 'sqlite';
}): SemanticLayerCompiler;
/**
* Factory function for creating SQLite executors
*/
export declare function createSQLiteExecutor(db: DrizzleDatabase, schema?: any): SQLiteExecutor;
/**
* Cube definition focused on Drizzle query building
*/
declare interface Cube {
name: string;
title?: string;
description?: string;
/**
* Base query setup - returns the foundation that can be extended
* Should return FROM/JOIN/WHERE setup, NOT a complete SELECT
*/
sql: (ctx: QueryContext) => BaseQueryDefinition;
/** Cube dimensions using direct column references */
dimensions: Record<string, Dimension>;
/** Cube measures using direct column references */
measures: Record<string, Measure>;
/** Optional joins to other cubes for multi-cube queries */
joins?: Record<string, CubeJoin>;
/** Whether cube is publicly accessible */
public?: boolean;
/** SQL alias for the cube */
sqlAlias?: string;
/** Data source identifier */
dataSource?: string;
/** Additional metadata */
meta?: Record<string, any>;
}
export { Cube }
export { Cube as SemanticCube }
/**
* Helper type for creating type-safe cubes
*/
export declare interface CubeDefiner {
<TName extends string>(name: TName, definition: CubeDefinition): Cube & {
name: TName;
};
}
/**
* Utility type for cube definition with schema inference
*/
export declare type CubeDefinition = Omit<Cube, 'name'> & {
name?: string;
};
/**
* Type-safe cube join definition with lazy loading support
*/
declare interface CubeJoin {
/** Target cube reference - lazy loaded to avoid circular dependencies */
targetCube: Cube | (() => Cube);
/** Semantic relationship - determines join behavior */
relationship: 'belongsTo' | 'hasOne' | 'hasMany';
/** Array of join conditions - supports multi-column joins */
on: Array<{
/** Column from source cube */
source: AnyColumn;
/** Column from target cube */
target: AnyColumn;
/** Comparison operator - defaults to eq */
as?: (source: AnyColumn, target: AnyColumn) => SQL;
}>;
/** Override default SQL join type (derived from relationship) */
sqlJoinType?: 'inner' | 'left' | 'right' | 'full';
}
export { CubeJoin }
export { CubeJoin as SemanticJoin }
/**
* Cube metadata for API responses
*/
export declare interface CubeMetadata {
name: string;
title: string;
description?: string;
measures: MeasureMetadata[];
dimensions: DimensionMetadata[];
segments: any[];
relationships?: CubeRelationshipMetadata[];
}
/**
* Cube relationship metadata for ERD visualization
*/
export declare interface CubeRelationshipMetadata {
targetCube: string;
relationship: 'belongsTo' | 'hasOne' | 'hasMany';
joinFields: Array<{
sourceField: string;
targetField: string;
}>;
}
declare interface DatabaseAdapter {
/**
* Get the database engine type this adapter supports
*/
getEngineType(): 'postgres' | 'mysql' | 'sqlite' | 'singlestore';
/**
* Build time dimension expression with granularity truncation
* @param granularity - Time granularity (day, month, year, etc.)
* @param fieldExpr - The date/timestamp field expression
* @returns SQL expression for truncated time dimension
*/
buildTimeDimension(granularity: TimeGranularity, fieldExpr: AnyColumn | SQL): SQL;
/**
* Build string matching condition
* @param fieldExpr - The field to search in
* @param operator - The string matching operator
* @param value - The value to match
* @returns SQL expression for string matching
*/
buildStringCondition(fieldExpr: AnyColumn | SQL, operator: 'contains' | 'notContains' | 'startsWith' | 'endsWith' | 'like' | 'notLike' | 'ilike' | 'regex' | 'notRegex', value: string): SQL;
/**
* Cast expression to specific database type
* @param fieldExpr - The field expression to cast
* @param targetType - Target database type
* @returns SQL expression with type casting
*/
castToType(fieldExpr: AnyColumn | SQL, targetType: 'timestamp' | 'decimal' | 'integer'): SQL;
/**
* Build AVG aggregation expression with database-specific null handling
* @param fieldExpr - The field expression to average
* @returns SQL expression for AVG aggregation (COALESCE vs IFNULL for null handling)
*/
buildAvg(fieldExpr: AnyColumn | SQL): SQL;
/**
* Build CASE WHEN conditional expression
* @param conditions - Array of condition/result pairs
* @param elseValue - Optional ELSE clause value
* @returns SQL expression for CASE WHEN statement
*/
buildCaseWhen(conditions: Array<{
when: SQL;
then: any;
}>, elseValue?: any): SQL;
/**
* Build boolean literal expression
* @param value - Boolean value to represent
* @returns SQL expression for boolean literal (TRUE/FALSE/1/0 depending on database)
*/
buildBooleanLiteral(value: boolean): SQL;
/**
* Convert filter values to database-compatible types
* @param value - The filter value to convert
* @returns Converted value for database queries
*/
convertFilterValue(value: any): any;
/**
* Prepare date value for database-specific storage format
* @param date - Date value to prepare
* @returns Database-compatible date representation
*/
prepareDateValue(date: Date): any;
/**
* Check if this database stores timestamps as integers
* @returns True if timestamps are stored as integers (milliseconds), false for native timestamps
*/
isTimestampInteger(): boolean;
/**
* Convert time dimension result values back to Date objects for consistency
* @param value - The time dimension value from query results
* @returns Date object or original value if not a time dimension
*/
convertTimeDimensionResult(value: any): any;
}
/**
* Database executor interface that wraps Drizzle ORM
* Provides type-safe SQL execution with engine-specific implementations
*/
export declare interface DatabaseExecutor {
/** The Drizzle database instance */
db: DrizzleDatabase;
/** Optional schema for type inference */
schema?: any;
/** Database adapter for SQL dialect-specific operations */
databaseAdapter: DatabaseAdapter;
/** Execute a Drizzle SQL query or query object */
execute<T = any[]>(query: SQL | any, numericFields?: string[]): Promise<T>;
/** Get the database engine type */
getEngineType(): 'postgres' | 'mysql' | 'sqlite' | 'singlestore';
}
/**
* Main semantic layer instance
* Use this for simple single-instance usage
*/
export declare const defaultSemanticLayer: SemanticLayerCompiler;
/**
* Helper function to create cubes
*/
export declare function defineCube(name: string, definition: Omit<Cube, 'name'>): Cube;
/**
* Dimension definition
*/
declare interface Dimension {
name: string;
title?: string;
description?: string;
type: DimensionType;
/** Direct column reference or SQL expression */
sql: AnyColumn | SQL | ((ctx: QueryContext) => AnyColumn | SQL);
/** Whether this is a primary key */
primaryKey?: boolean;
/** Whether to show in UI */
shown?: boolean;
/** Display format */
format?: string;
/** Additional metadata */
meta?: Record<string, any>;
}
export { Dimension }
export { Dimension as SemanticDimension }
export declare interface DimensionAnnotation {
title: string;
shortTitle: string;
type: string;
format?: DimensionFormat;
}
export declare type DimensionFormat = 'currency' | 'percent' | 'number' | 'date' | 'datetime' | 'id' | 'link';
/**
* Dimension metadata for API responses
*/
export declare interface DimensionMetadata {
name: string;
title: string;
shortTitle: string;
type: string;
format?: DimensionFormat;
description?: string;
}
export declare type DimensionType = 'string' | 'number' | 'time' | 'boolean';
/**
* Drizzle column reference type
* Use native Drizzle AnyColumn type for better type safety
*/
export declare type DrizzleColumn = AnyColumn;
/**
* Core database interface that supports all Drizzle instances
* Uses flexible typing for maximum compatibility across different database drivers
*/
export declare interface DrizzleDatabase {
select: (fields?: any) => any;
insert: (table: any) => any;
update: (table: any) => any;
delete: (table: any) => any;
execute?: (query: SQL) => Promise<unknown[]>;
run?: (query: SQL) => unknown;
all?: (query: SQL) => unknown[];
get?: (query: SQL) => unknown;
$with: (alias: string) => {
as: (query: any) => any;
};
with: (...args: any[]) => any;
schema?: unknown;
}
/**
* Type-level utility to extract the schema type from a cube reference
* Since we removed generics, this now returns 'any'
*/
export declare type ExtractSchemaFromCubeRef = any;
/**
* Filter definitions with logical operators
*/
export declare type Filter = FilterCondition | LogicalFilter;
export declare interface FilterCondition {
member: string;
operator: FilterOperator;
values: any[];
}
/**
* Supported filter operators
*/
export declare type FilterOperator = 'equals' | 'notEquals' | 'contains' | 'notContains' | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith' | 'gt' | 'gte' | 'lt' | 'lte' | 'set' | 'notSet' | 'inDateRange' | 'beforeDate' | 'afterDate' | 'between' | 'notBetween' | 'in' | 'notIn' | 'like' | 'notLike' | 'ilike' | 'regex' | 'notRegex' | 'isEmpty' | 'isNotEmpty';
/**
* Derive SQL join type from semantic relationship
*/
export declare function getJoinType(relationship: string, override?: string): string;
export declare type JoinType = 'left' | 'right' | 'inner' | 'full';
export declare interface LogicalFilter {
and?: Filter[];
or?: Filter[];
}
/**
* Measure definition
*/
declare interface Measure {
name: string;
title?: string;
description?: string;
type: MeasureType;
/** Column to aggregate or SQL expression */
sql: AnyColumn | SQL | ((ctx: QueryContext) => AnyColumn | SQL);
/** Display format */
format?: string;
/** Whether to show in UI */
shown?: boolean;
/** Filters applied to this measure */
filters?: Array<(ctx: QueryContext) => SQL>;
/** Rolling window configuration */
rollingWindow?: {
trailing?: string;
leading?: string;
offset?: string;
};
/** Additional metadata */
meta?: Record<string, any>;
}
export { Measure }
export { Measure as SemanticMeasure }
/**
* Annotation interfaces for UI metadata
*/
export declare interface MeasureAnnotation {
title: string;
shortTitle: string;
type: MeasureType;
format?: MeasureFormat;
}
export declare type MeasureFormat = 'currency' | 'percent' | 'number' | 'integer';
/**
* Measure metadata for API responses
*/
export declare interface MeasureMetadata {
name: string;
title: string;
shortTitle: string;
type: MeasureType;
format?: MeasureFormat;
description?: string;
}
/**
* Type enums and constants
*/
export declare type MeasureType = 'count' | 'countDistinct' | 'countDistinctApprox' | 'sum' | 'avg' | 'min' | 'max' | 'runningTotal' | 'number';
/**
* Multi-cube query context for cross-cube operations
*/
export declare interface MultiCubeQueryContext extends QueryContext {
/** Available cubes for cross-cube operations */
cubes: Map<string, Cube>;
/** Current cube being processed */
currentCube: Cube;
}
export declare type MySQLDatabase = MySql2Database<any>;
export declare class MySQLExecutor extends BaseDatabaseExecutor {
execute<T = any[]>(query: SQL | any, numericFields?: string[]): Promise<T>;
/**
* Convert numeric string fields to numbers (measure fields + numeric dimensions)
*/
private convertNumericFields;
/**
* Coerce a value to a number if it represents a numeric type
*/
private coerceToNumber;
getEngineType(): 'mysql' | 'singlestore';
}
/**
* Type helpers for specific database types
*/
export declare type PostgresDatabase = PostgresJsDatabase<any>;
export declare class PostgresExecutor extends BaseDatabaseExecutor {
execute<T = any[]>(query: SQL | any, numericFields?: string[]): Promise<T>;
/**
* Convert numeric string fields to numbers (only for measure fields)
*/
private convertNumericFields;
/**
* Coerce a value to a number if it represents a numeric type
*/
private coerceToNumber;
getEngineType(): 'postgres';
}
/**
* Any queryable relation that can be used in FROM/JOIN clauses
* Supports tables, views, subqueries, and raw SQL expressions
*/
export declare type QueryableRelation = Table | View | Subquery | SQL;
export declare class QueryBuilder {
private databaseAdapter;
constructor(databaseAdapter: DatabaseAdapter);
/**
* Build dynamic selections for measures, dimensions, and time dimensions
* Works for both single and multi-cube queries
*/
buildSelections(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext): Record<string, SQL | AnyColumn>;
/**
* Build measure expression for HAVING clause, handling CTE references correctly
*/
private buildHavingMeasureExpression;
/**
* Build measure expression with aggregation and filters
*/
buildMeasureExpression(measure: any, context: QueryContext): SQL;
/**
* Build time dimension expression with granularity using database adapter
*/
buildTimeDimensionExpression(dimensionSql: any, granularity: string | undefined, context: QueryContext): SQL;
/**
* Build WHERE conditions from semantic query filters (dimensions only)
* Works for both single and multi-cube queries
*/
buildWhereConditions(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: QueryPlan): SQL[];
/**
* Build HAVING conditions from semantic query filters (measures only)
* Works for both single and multi-cube queries
*/
buildHavingConditions(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: QueryPlan): SQL[];
/**
* Process a single filter (basic or logical)
* @param filterType - 'where' for dimension filters, 'having' for measure filters
*/
private processFilter;
/**
* Build filter condition using Drizzle operators
*/
private buildFilterCondition;
/**
* Build date range condition for time dimensions
*/
buildDateRangeCondition(fieldExpr: AnyColumn | SQL, dateRange: string | string[]): SQL | null;
/**
* Parse relative date range expressions like "today", "yesterday", "last 7 days", "this month", etc.
* Handles all 14 DATE_RANGE_OPTIONS from the client
*/
private parseRelativeDateRange;
/**
* Normalize date values to handle strings, numbers, and Date objects
* Always returns a JavaScript Date object or null
* Database-agnostic - just ensures we have a valid Date
*/
private normalizeDate;
/**
* Build GROUP BY fields from dimensions and time dimensions
* Works for both single and multi-cube queries
*/
buildGroupByFields(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: any): (SQL | AnyColumn)[];
/**
* Build ORDER BY clause with automatic time dimension sorting
*/
buildOrderBy(query: SemanticQuery, selectedFields?: string[]): SQL[];
/**
* Collect numeric field names (measures + numeric dimensions) for type conversion
* Works for both single and multi-cube queries
*/
collectNumericFields(cubes: Map<string, Cube> | Cube, query: SemanticQuery): string[];
/**
* Apply LIMIT and OFFSET to a query with validation
* If offset is provided without limit, add a reasonable default limit
*/
applyLimitAndOffset<T>(query: T, semanticQuery: SemanticQuery): T;
}
/**
* Query context passed to cube SQL functions
* Provides access to database, schema, and security context
*/
declare interface QueryContext {
/** Drizzle database instance */
db: DrizzleDatabase;
/** Database schema (tables, columns, etc.) */
schema?: any;
/** Security context for filtering */
securityContext: SecurityContext;
/** The semantic query being executed */
query?: SemanticQuery;
/** The compiled cube being queried */
cube?: Cube;
}
export { QueryContext }
export { QueryContext as SemanticQueryContext }
export declare class QueryExecutor {
private dbExecutor;
private queryBuilder;
private queryPlanner;
private databaseAdapter;
constructor(dbExecutor: DatabaseExecutor);
/**
* Unified query execution method that handles both single and multi-cube queries
*/
execute(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<QueryResult>;
/**
* Legacy interface for single cube queries
*/
executeQuery(cube: Cube, query: SemanticQuery, securityContext: SecurityContext): Promise<QueryResult>;
/**
* Build pre-aggregation CTE for hasMany relationships
*/
private buildPreAggregationCTE;
/**
* Build join condition for CTE
*/
private buildCTEJoinCondition;
/**
* Build unified query that works for both single and multi-cube queries
*/
private buildUnifiedQuery;
/**
* Convert query plan to cube map for QueryBuilder methods
*/
private getCubesFromPlan;
/**
* Generate raw SQL for debugging (without execution) - unified approach
*/
generateSQL(cube: Cube, query: SemanticQuery, securityContext: SecurityContext): Promise<{
sql: string;
params?: any[];
}>;
/**
* Generate raw SQL for multi-cube queries without execution - unified approach
*/
generateMultiCubeSQL(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
sql: string;
params?: any[];
}>;
/**
* Generate SQL using unified approach (works for both single and multi-cube)
*/
private generateUnifiedSQL;
/**
* Generate annotations for UI metadata - unified approach
*/
private generateAnnotations;
}
/**
* Unified Query Plan for both single and multi-cube queries
* - For single-cube queries: joinCubes array is empty
* - For multi-cube queries: joinCubes contains the additional cubes to join
* - selections, whereConditions, and groupByFields are populated by QueryBuilder
*/
export declare interface QueryPlan {
/** Primary cube that drives the query */
primaryCube: Cube;
/** Additional cubes to join (empty for single-cube queries) */
joinCubes: Array<{
cube: Cube;
alias: string;
joinType: 'inner' | 'left' | 'right' | 'full';
joinCondition: SQL;
}>;
/** Combined field selections across all cubes (built by QueryBuilder) */
selections: Record<string, SQL | AnyColumn>;
/** WHERE conditions for the entire query (built by QueryBuilder) */
whereConditions: SQL[];
/** GROUP BY fields if aggregations are present (built by QueryBuilder) */
groupByFields: (SQL | AnyColumn)[];
/** Pre-aggregation CTEs for hasMany relationships to prevent fan-out */
preAggregationCTEs?: Array<{
cube: Cube;
alias: string;
cteAlias: string;
joinKeys: Array<{
sourceColumn: string;
targetColumn: string;
sourceColumnObj?: AnyColumn;
targetColumnObj?: AnyColumn;
}>;
measures: string[];
}>;
}
/**
* Pre-aggregation plan for handling hasMany relationships
*/
export declare class QueryPlanner {
/**
* Analyze a semantic query to determine which cubes are involved
*/
analyzeCubeUsage(query: SemanticQuery): Set<string>;
/**
* Recursively extract cube names from filters (handles logical filters)
*/
private extractCubeNamesFromFilter;
/**
* Extract measures referenced in filters (for CTE inclusion)
*/
private extractMeasuresFromFilters;
/**
* Recursively extract measures from filters for a specific cube
*/
private extractMeasuresFromFilter;
/**
* Create a unified query plan that works for both single and multi-cube queries
*/
createQueryPlan(cubes: Map<string, Cube>, query: SemanticQuery, _ctx: QueryContext): QueryPlan;
/**
* Choose the primary cube based on query analysis
* Uses a consistent strategy to avoid measure order dependencies
*/
choosePrimaryCube(cubeNames: string[], query: SemanticQuery, cubes?: Map<string, Cube>): string;
/**
* Check if a cube can reach all other cubes in the list via joins
*/
private canReachAllCubes;
/**
* Build join plan for multi-cube query
* Supports both direct joins and transitive joins through intermediate cubes
*/
private buildJoinPlan;
/**
* Build join condition from new array-based join definition
*/
private buildJoinCondition;
/**
* Find join path from source cube to target cube
* Returns array of join steps to reach target
*/
private findJoinPath;
/**
* Plan pre-aggregation CTEs for hasMany relationships to prevent fan-out
*/
private planPreAggregationCTEs;
/**
* Find hasMany join definition from primary cube to target cube
*/
private findHasManyJoinDef;
}
/**
* Query execution result
*/
export declare interface QueryResult {
data: Record<string, unknown>[];
annotation: {
measures: Record<string, MeasureAnnotation>;
dimensions: Record<string, DimensionAnnotation>;
segments: Record<string, unknown>;
timeDimensions: Record<string, TimeDimensionAnnotation>;
};
}
/**
* Resolve cube reference (handles both direct and lazy references)
*/
export declare function resolveCubeReference(ref: Cube | (() => Cube)): Cube;
/**
* Helper to resolve SQL expressions
*/
export declare function resolveSqlExpression(expr: AnyColumn | SQL | ((ctx: QueryContext) => AnyColumn | SQL), ctx: QueryContext): AnyColumn | SQL;
/**
* Security context passed to cube SQL functions
* Contains user/tenant-specific data for filtering
*/
export declare interface SecurityContext {
[key: string]: unknown;
}
export declare const semanticLayer: SemanticLayerCompiler;
export declare class SemanticLayerCompiler {
private cubes;
private dbExecutor?;
private metadataCache?;
private metadataCacheTimestamp?;
private readonly METADATA_CACHE_TTL;
constructor(options?: {
drizzle?: DatabaseExecutor['db'];
schema?: any;
databaseExecutor?: DatabaseExecutor;
engineType?: 'postgres' | 'mysql' | 'sqlite' | 'singlestore';
});
/**
* Set or update the database executor
*/
setDatabaseExecutor(executor: DatabaseExecutor): void;
/**
* Get the database engine type for SQL formatting
*/
getEngineType(): 'postgres' | 'mysql' | 'sqlite' | 'singlestore' | undefined;
/**
* Set Drizzle instance and schema directly
*/
setDrizzle(db: DatabaseExecutor['db'], schema?: any, engineType?: 'postgres' | 'mysql' | 'sqlite' | 'singlestore'): void;
/**
* Check if database executor is configured
*/
hasExecutor(): boolean;
/**
* Register a simplified cube with dynamic query building
*/
registerCube(cube: Cube): void;
/**
* Get a cube by name
*/
getCube(name: string): Cube | undefined;
/**
* Get all registered cubes
*/
getAllCubes(): Cube[];
/**
* Get all cubes as a Map for multi-cube queries
*/
getAllCubesMap(): Map<string, Cube>;
/**
* Unified query execution method that handles both single and multi-cube queries
*/
execute(query: SemanticQuery, securityContext: SecurityContext): Promise<QueryResult>;
/**
* Execute a multi-cube query
*/
executeMultiCubeQuery(query: SemanticQuery, securityContext: SecurityContext): Promise<QueryResult>;
/**
* Execute a single cube query
*/
executeQuery(cubeName: string, query: SemanticQuery, securityContext: SecurityContext): Promise<QueryResult>;
/**
* Get metadata for all cubes (for API responses)
* Uses caching to improve performance for repeated requests
*/
getMetadata(): CubeMetadata[];
/**
* Extract column name from Drizzle column reference
* Handles different column types and extracts the actual column name
*/
private getColumnName;
/**
* Generate cube metadata for API responses from cubes
* Optimized version that minimizes object iterations
*/
private generateCubeMetadata;
/**
* Get SQL for a query without executing it (debugging)
*/
generateSQL(cubeName: string, query: SemanticQuery, securityContext: SecurityContext): Promise<{
sql: string;
params?: any[];
}>;
/**
* Get SQL for a multi-cube query without executing it (debugging)
*/
generateMultiCubeSQL(query: SemanticQuery, securityContext: SecurityContext): Promise<{
sql: string;
params?: any[];
}>;
/**
* Check if a cube exists
*/
hasCube(name: string): boolean;
/**
* Remove a cube
*/
removeCube(name: string): boolean;
/**
* Clear all cubes
*/
clearCubes(): void;
/**
* Invalidate the metadata cache
* Called whenever cubes are modified
*/
private invalidateMetadataCache;
/**
* Get cube names
*/
getCubeNames(): string[];
/**
* Validate a query against registered cubes
* Ensures all referenced cubes and fields exist
*/
validateQuery(query: SemanticQuery): {
isValid: boolean;
errors: string[];
};
}
/**
* Utility functions for working with the semantic layer
*/
export declare const SemanticLayerUtils: {
/**
* Create a simple query builder
*/
query: () => {
measures: (measures: string[]) => {
dimensions: (dimensions?: string[]) => {
filters: (filters?: any[]) => {
timeDimensions: (timeDimensions?: any[]) => {
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
timeDimensions: (timeDimensions?: any[]) => {
filters: (filters?: any[]) => {
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
filters: (filters?: any[]) => {
dimensions: (dimensions?: string[]) => {
timeDimensions: (timeDimensions?: any[]) => {
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
timeDimensions: (timeDimensions?: any[]) => {
dimensions: (dimensions?: string[]) => {
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
limit: (limit?: number) => {
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
order: (order?: Record<string, "asc" | "desc">) => {
measures: string[];
dimensions: string[];
filters: any[];
timeDimensions: any[];
limit: number | undefined;
order: Record<string, "asc" | "desc"> | undefined;
};
};
};
};
/**
* Create filters
*/
filters: {
equals: (member: string, value: any) => {
member: string;
operator: "equals";
values: any[];
};
notEquals: (member: string, value: any) => {
member: string;
operator: "notEquals";
values: any[];
};
contains: (member: string, value: string) => {
member: string;
operator: "contains";
values: string[];
};
greaterThan: (member: string, value: any) => {
member: string;
operator: "gt";
values: any[];
};
lessThan: (member: string, value: any) => {
member: string;
operator: "lt";
values: any[];
};
inDateRange: (member: string, from: string, to: string) => {
member: string;
operator: "inDateRange";
values: string[];
};
set: (member: string) => {
member: string;
operator: "set";
values: never[];
};
notSet: (member: string) => {
member: string;
operator: "notSet";
values: never[];
};
};
/**
* Create time dimensions
*/
timeDimensions: {
create: (dimension: string, granularity?: TimeGranularity, dateRange?: string | string[]) => {
dimension: string;
granularity: TimeGranularity | undefined;
dateRange: string | string[] | undefined;
};
};
};
/**
* Pre-aggregation configuration
*/
export declare interface SemanticPreAggregation {
name: string;
measures: string[];
dimensions: string[];
timeDimension?: {
dimension: string;
granularity: TimeGranularity[];
};
refreshKey?: {
every: string;
sql?: string;
};
indexes?: Record<string, string[]>;
}
/**
* Semantic query structure (Cube.js compatible)
*/
export declare interface SemanticQuery {
measures?: string[];
dimensions?: string[];
filters?: Array<Filter>;
timeDimensions?: Array<TimeDimension>;
limit?: number;
offset?: number;
order?: Record<string, 'asc' | 'desc'>;
}
export { SQL }
/**
* Type for SQL expressions that can be functions or direct values
*/
export declare type SqlExpression = AnyColumn | SQL | ((ctx: QueryContext) => AnyColumn | SQL);
export declare type SQLiteDatabase = BetterSQLite3Database<any>;
export declare class SQLiteExecutor extends BaseDatabaseExecutor {
execute<T = any[]>(query: SQL | any, numericFields?: string[]): Promise<T>;
/**
* Convert numeric string fields to numbers (only for measure fields)
*/
private convertNumericFields;
/**
* Coerce a value to a number if it represents a numeric type
*/
private coerceToNumber;
getEngineType(): 'sqlite';
}
/**
* SQL generation result
*/
export declare interface SqlResult {
sql: string;
params?: unknown[];
}
/**
* Time dimension with granularity
*/
export declare interface TimeDimension {
dimension: string;
granularity?: TimeGranularity;
dateRange?: string | string[];
}
export declare interface TimeDimensionAnnotation {
title: string;
shortTitle: string;
type: string;
granularity?: TimeGranularity;
}
export declare type TimeGranularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';
export { }