UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

377 lines 14.2 kB
/** * Intelligent Query Engine - Main Entry Point * * The Intelligent Query Engine provides a universal, extensible query system * that automatically discovers field locations, plans optimal execution strategies, * and executes queries across different data models. */ import { EventEmitter } from 'events'; import { getLogger } from '../../logging/Logger.js'; import { FieldCatalog } from './FieldCatalog.js'; import { QueryPlanner } from './QueryPlanner.js'; import { HybridExecutor } from './HybridExecutor.js'; import { CacheOrchestrator } from './cache/index.js'; import { InvalidationEngine } from './cache/InvalidationEngine.js'; import { SyncCacheIntegration } from './cache/SyncCacheIntegration.js'; const logger = getLogger(); /** * Main Intelligent Query Engine class */ export class IntelligentQueryEngine extends EventEmitter { fieldCatalog; queryPlanner; hybridExecutor; adapters; queryPatterns; config; cacheOrchestrator; invalidationEngine; syncCacheIntegration; constructor(config = {}) { super(); // Initialize configuration with defaults this.config = { discovery: { autoDiscover: true, discoveryInterval: 3600000, // 1 hour cacheTTL: 3600000 }, execution: { defaultStrategy: 'hybrid-sql-first', maxQueryTime: 30000, // 30 seconds enableParallel: true, parallelThreshold: 1000 }, learning: { enabled: true, sampleRate: 0.1, // 10% of queries optimizationThreshold: 5 }, adapters: {}, ...config }; // Initialize components this.adapters = new Map(); this.queryPatterns = new Map(); this.fieldCatalog = new FieldCatalog({ cacheSize: 1000, cacheTTL: this.config.discovery.cacheTTL, autoDiscovery: this.config.discovery.autoDiscover }); this.queryPlanner = new QueryPlanner(this.fieldCatalog, this.adapters, { defaultStrategy: this.config.execution.defaultStrategy, enableParallel: this.config.execution.enableParallel }); this.hybridExecutor = new HybridExecutor(this.adapters, this.fieldCatalog, { maxExecutionTime: this.config.execution.maxQueryTime, enableParallel: this.config.execution.enableParallel, cacheResults: false // Disabled - using CacheOrchestrator instead }); // Initialize cache orchestrator this.cacheOrchestrator = new CacheOrchestrator({ enabled: config.cache?.enabled ?? true, warmupOnStart: config.cache?.warmupOnStart ?? false, statsInterval: config.cache?.statsInterval ?? 300000, }); // Listen to cache events for monitoring this.cacheOrchestrator.on('cache-event', (event) => { if (event.type === 'hit' || event.type === 'miss') { logger.debug(`Cache ${event.type} for query: ${event.key}`); } }); this.cacheOrchestrator.on('stats', (stats) => { logger.info(`Cache statistics: ${JSON.stringify(stats)}`); }); // Initialize invalidation engine this.invalidationEngine = new InvalidationEngine(this.cacheOrchestrator); // Initialize sync integration if enabled if (config.cache?.syncIntegration?.enabled ?? true) { this.syncCacheIntegration = new SyncCacheIntegration(this.invalidationEngine, config.cache?.syncIntegration); } logger.info('IntelligentQueryEngine initialized with caching and invalidation'); } /** * Register a data model adapter */ registerAdapter(adapter) { logger.info(`Registering adapter: ${adapter.name}`); // Store adapter this.adapters.set(adapter.name, adapter); // Register with field catalog this.fieldCatalog.registerAdapter(adapter); // Apply adapter-specific configuration if (this.config.adapters[adapter.name]) { logger.debug(`Applying configuration for adapter ${adapter.name}`, this.config.adapters[adapter.name]); } // Emit event this.emit('adapter:registered', adapter); logger.info(`Adapter registered successfully: ${adapter.name}`); } /** * Execute a query */ async query(query, options) { const queryStart = Date.now(); logger.info('Executing query'); // Emit start event this.emit('query:start', query); try { // Execute with cache const cacheResult = await this.cacheOrchestrator.executeWithCache(query, async (queryToExecute) => { // Create execution plan const plan = await this.queryPlanner.createPlan(queryToExecute); // Emit plan event this.emit('query:plan', plan); // Execute plan const result = await this.hybridExecutor.execute(plan); // Track performance if learning is enabled if (this.config.learning.enabled && Math.random() < this.config.learning.sampleRate) { this.trackPerformance(queryToExecute, result, queryStart); } return result; }, options?.context, { bypassCache: options?.bypassCache, refreshCache: options?.refreshCache, ttlFactors: options?.ttlFactors, }); // Build final result with cache metadata // cacheResult.data is the QueryResult from the executor const result = { ...cacheResult.data, metadata: { ...(cacheResult.data.metadata || {}), cached: cacheResult.cached, cacheKey: cacheResult.cacheKey, cacheTTL: cacheResult.ttl, totalExecutionTime: Date.now() - queryStart, } }; // Emit complete event this.emit('query:complete', result); logger.info(`Query executed successfully (cached: ${cacheResult.cached})`); return result; } catch (error) { logger.error(`Query execution failed: ${error}`); // Emit error event this.emit('query:error', error); throw error; } } /** * Analyze a query without executing it */ async analyze(query) { logger.info('Analyzing query'); try { const plan = await this.queryPlanner.createPlan(query); logger.info('Query analysis complete'); return plan; } catch (error) { logger.error(`Query analysis failed: ${error}`); throw error; } } /** * Get field information for an entity */ async describeEntity(entityName) { logger.info(`Describing entity: ${entityName}`); const fields = new Set(); const relationships = new Set(); // Try each adapter for (const [name, adapter] of this.adapters) { try { // Get entities const entities = await adapter.discoverEntities(); const entity = entities.find(e => e.name === entityName); if (!entity) continue; // Get fields const adapterFields = await adapter.discoverFields(entityName); adapterFields.forEach(f => fields.add(f.name)); // Get relationships const adapterRelationships = await adapter.discoverRelationships(); if (adapterRelationships[entityName]) { adapterRelationships[entityName].forEach(r => { relationships.add(`${r.to.entity} (via ${r.from.field})`); }); } } catch (error) { logger.warn(`Failed to describe entity from adapter ${name}: ${error}`); } } return { fields: Array.from(fields), relationships: Array.from(relationships) }; } /** * Track query performance for learning */ trackPerformance(query, result, startTime) { const queryHash = this.hashQuery(query); // Update pattern statistics let pattern = this.queryPatterns.get(queryHash); if (!pattern) { pattern = { hash: queryHash, query, executionCount: 0, averageExecutionTime: 0, lastExecuted: new Date(), optimalStrategy: result.plan?.strategy }; this.queryPatterns.set(queryHash, pattern); } // Update statistics pattern.executionCount++; pattern.averageExecutionTime = (pattern.averageExecutionTime * (pattern.executionCount - 1) + result.metadata.executionTime) / pattern.executionCount; pattern.lastExecuted = new Date(); // Create performance metric const metric = { queryHash, executionTime: result.metadata.executionTime, rowsReturned: result.metadata.rowCount, memoryUsed: 0, // Would be tracked in real implementation cacheHit: result.metadata.cacheHit, timestamp: new Date() }; // Emit metric event this.emit('performance:metric', metric); // Check if we should optimize if (pattern.executionCount >= this.config.learning.optimizationThreshold) { this.optimizeQueryPattern(pattern); } } /** * Optimize a frequently used query pattern */ optimizeQueryPattern(pattern) { logger.info('Optimizing query pattern'); // In a real implementation, this would: // 1. Analyze execution history // 2. Test alternative strategies // 3. Create materialized views or indexes // 4. Update planner hints } /** * Hash a query for pattern matching */ hashQuery(query) { // Simple hash - would be more sophisticated in real implementation return JSON.stringify({ find: query.find, select: query.select?.sort(), whereFields: query.where?.map(w => w.field).sort(), groupBy: query.groupBy?.sort(), aggregations: query.aggregations?.map(a => a.function) }); } /** * Get engine statistics */ getStatistics() { return { adapters: Array.from(this.adapters.keys()), queryPatterns: this.queryPatterns.size, fieldCatalog: this.fieldCatalog.getStatistics(), cache: this.cacheOrchestrator.getStats() }; } /** * Clear all caches */ clearCaches() { logger.info('Clearing all caches'); this.fieldCatalog.clearCache(); this.queryPatterns.clear(); this.cacheOrchestrator.clearCache(); } /** * Invalidate cache for specific entities */ async invalidateCache(entityType, operation, context) { if (entityType) { return this.cacheOrchestrator.invalidateEntity(entityType, operation, context); } else { // Invalidate all if no entity specified this.cacheOrchestrator.clearCache(); return -1; // Indicates full clear } } /** * Preload cache with common queries */ async preloadCache(queries) { logger.info(`Preloading cache with ${queries.length} queries`); const promises = queries.map(query => this.query(query, { refreshCache: true }).catch(err => { logger.warn(`Failed to preload query: ${err.message}`); })); await Promise.all(promises); logger.info('Cache preload complete'); } /** * Get cache metrics */ getCacheMetrics() { const stats = this.cacheOrchestrator.getStats(); return { hitRate: stats?.hitRate || 0, entries: stats?.entries || 0, sizeBytes: stats?.sizeBytes || 0, recommendedTTLs: { 'flag-list': this.cacheOrchestrator.getRecommendedTTL('flag-list'), 'experiment-details': this.cacheOrchestrator.getRecommendedTTL('experiment-details'), 'daily-report': this.cacheOrchestrator.getRecommendedTTL('daily-report'), 'realtime-dashboard': this.cacheOrchestrator.getRecommendedTTL('realtime-dashboard'), } }; } /** * Get sync cache integration handler for IncrementalSyncManager */ getSyncCacheHandler() { if (!this.syncCacheIntegration) { logger.warn('Sync cache integration is not enabled'); return null; } return this.syncCacheIntegration.createSyncHandler(); } /** * Get invalidation engine for direct access */ getInvalidationEngine() { return this.invalidationEngine; } /** * Shutdown the engine */ async shutdown() { logger.info('Shutting down IntelligentQueryEngine'); // Shutdown sync integration if (this.syncCacheIntegration) { await this.syncCacheIntegration.shutdown(); } // Shutdown invalidation engine this.invalidationEngine.shutdown(); // Shutdown cache orchestrator this.cacheOrchestrator.shutdown(); // Clear caches this.clearCaches(); // Remove all listeners this.removeAllListeners(); logger.info('IntelligentQueryEngine shutdown complete'); } } // Export all types for convenience export * from './types.js'; export { FieldCatalog } from './FieldCatalog.js'; export { QueryPlanner } from './QueryPlanner.js'; export { HybridExecutor } from './HybridExecutor.js'; //# sourceMappingURL=IntelligentQueryEngine.js.map