@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
377 lines • 14.2 kB
JavaScript
/**
* 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