UNPKG

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
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 { }