UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

375 lines 14.7 kB
/** * Field Resolution Strategy - JOIN Necessity Algorithm * * CRITICAL COMPONENT: Prevents JOIN explosion by implementing smart field resolution * * DESIGN PRINCIPLES: * 1. Local First: Check primary entity fields before considering JOINs * 2. Aggregation Aware: Different logic for COUNT/SUM vs detail queries * 3. Cardinality Conscious: Prevent multiplication explosions * 4. Necessity Driven: Require explicit need for JOINs, not just availability * * Created: July 5, 2025 * Purpose: Fix 700-1400x count inflation caused by unnecessary JOINs */ import { getLogger } from '../../logging/Logger.js'; const logger = getLogger(); /** * JOIN Necessity Decision Tree Implementation */ export class FieldResolutionAlgorithm { localFieldMaps = new Map(); dangerousJoins = new Set(); constructor() { this.initializeLocalFieldMaps(); this.initializeDangerousJoins(); } /** * Main field resolution method using necessity-driven algorithm */ resolveFieldWithPriority(field, context) { logger.info(`Resolving field '${field}' for entity '${context.primaryEntity}' (${context.queryType})`); // STEP 1: Check if field exists locally (HIGHEST PRIORITY) const localResult = this.checkLocalField(field, context); if (localResult.confidence > 0.8) { logger.info(`Field '${field}' resolved locally: ${localResult.fieldPath}`); return localResult; } // STEP 2: Check if aggregation can be done without JOIN if (context.isAggregation) { const aggregationResult = this.checkAggregationWithoutJoin(field, context); if (aggregationResult.confidence > 0.7) { logger.info(`Aggregation for '${field}' resolved without JOIN: ${aggregationResult.fieldPath}`); return aggregationResult; } } // STEP 3: Evaluate JOIN necessity (CAREFUL EVALUATION) const joinResult = this.evaluateJoinNecessity(field, context); if (joinResult.confidence > 0.6 && this.isJoinSafe(joinResult.joinPath)) { logger.info(`Field '${field}' requires JOIN: ${joinResult.fieldPath}`); return joinResult; } // STEP 4: Fallback to simple field (SAFETY NET) const fallbackResult = this.createFallbackResolution(field, context); logger.warn(`🔄 Field '${field}' using fallback: ${fallbackResult.fieldPath}`); return fallbackResult; } /** * Step 1: Check if field exists in primary entity */ checkLocalField(field, context) { const localFields = this.localFieldMaps.get(context.primaryEntity) || []; // Direct field match if (localFields.includes(field)) { return { source: 'local', fieldPath: field, joinRequired: false, estimatedCost: 1, confidence: 1.0, reasoning: `Field '${field}' exists directly in ${context.primaryEntity} table` }; } // Common field variations (environment_key, environment, env) const fieldVariations = this.generateFieldVariations(field); for (const variation of fieldVariations) { if (localFields.includes(variation)) { return { source: 'local', fieldPath: variation, joinRequired: false, estimatedCost: 1, confidence: 0.9, reasoning: `Field variation '${variation}' found locally for '${field}'` }; } } // Check junction table patterns (flag_environments for flags + environment) const junctionResult = this.checkJunctionTableLocal(field, context); if (junctionResult.confidence > 0.8) { return junctionResult; } return { source: 'local', fieldPath: field, joinRequired: false, estimatedCost: 1, confidence: 0.1, reasoning: `Field '${field}' not found locally in ${context.primaryEntity}` }; } /** * Step 2: Check if aggregation can be done without JOIN */ checkAggregationWithoutJoin(field, context) { // Special handling for environment fields in flags context if (context.primaryEntity === 'flags' && field === 'environment') { return { source: 'local', fieldPath: 'flag_environments.environment_key', joinRequired: false, // Use flag_environments directly, no JOIN to environments table estimatedCost: 2, confidence: 0.95, reasoning: 'Environment field for flags can be aggregated from flag_environments without JOIN' }; } // Special handling for project fields if (field === 'project' || field === 'project_id') { return { source: 'local', fieldPath: 'project_id', joinRequired: false, estimatedCost: 1, confidence: 0.9, reasoning: 'Project field available locally in most entities' }; } // Check for common aggregation patterns that don't need JOINs const aggregationPatterns = { 'status': 'status', 'enabled': 'enabled', 'archived': 'archived', 'created': 'created', 'last_modified': 'last_modified' }; if (field in aggregationPatterns) { return { source: 'local', fieldPath: aggregationPatterns[field], joinRequired: false, estimatedCost: 1, confidence: 0.8, reasoning: `Common aggregation field '${field}' typically available locally` }; } return { source: 'local', fieldPath: field, joinRequired: false, estimatedCost: 1, confidence: 0.2, reasoning: `No aggregation-specific resolution found for '${field}'` }; } /** * Step 3: Evaluate if JOIN is truly necessary */ evaluateJoinNecessity(field, context) { // DANGER: Avoid these JOINs that cause explosion if (this.dangerousJoins.has(`${context.primaryEntity}_${field}`)) { return { source: 'fallback', fieldPath: field, joinRequired: false, estimatedCost: 1, confidence: 0.1, reasoning: `JOIN for '${field}' marked as dangerous (causes explosion)` }; } // Evaluate potential JOIN paths const potentialJoins = this.getPotentialJoins(context.primaryEntity, field); for (const join of potentialJoins) { // Skip JOINs that would cause multiplication for aggregations if (context.isAggregation && join.estimatedMultiplier > 2) { logger.warn(`Skipping JOIN to ${join.toTable} - would cause ${join.estimatedMultiplier}x multiplication in aggregation`); continue; } // Skip JOINs to large lookup tables for COUNT queries if (context.queryType === 'count' && this.isLargeLookupTable(join.toTable)) { logger.warn(`Skipping JOIN to large lookup table ${join.toTable} for COUNT query`); continue; } return { source: 'related', fieldPath: `${join.toTable}.${field}`, joinRequired: true, joinPath: join, estimatedCost: join.estimatedMultiplier * 10, confidence: 0.6, reasoning: `Field '${field}' requires JOIN to ${join.toTable} (${join.estimatedMultiplier}x multiplier)` }; } return { source: 'fallback', fieldPath: field, joinRequired: false, estimatedCost: 1, confidence: 0.1, reasoning: `No safe JOIN path found for '${field}'` }; } /** * Step 4: Create fallback resolution */ createFallbackResolution(field, context) { return { source: 'fallback', fieldPath: field, joinRequired: false, estimatedCost: 1, confidence: 0.5, reasoning: `Using fallback resolution for '${field}' - field used as-is` }; } /** * Check if a JOIN is safe (won't cause explosion) */ isJoinSafe(joinPath) { if (!joinPath) return false; // Reject JOINs with high multiplication factors if (joinPath.estimatedMultiplier > 5) { logger.warn(`JOIN rejected - high multiplication factor: ${joinPath.estimatedMultiplier}x`); return false; } // Reject JOINs to known dangerous tables const dangerousTables = ['environments', 'projects', 'audiences']; if (dangerousTables.includes(joinPath.toTable)) { logger.warn(`JOIN rejected - dangerous table: ${joinPath.toTable}`); return false; } return true; } /** * Generate field variations for fuzzy matching */ generateFieldVariations(field) { const variations = [field]; // Add common variations if (field === 'environment') { variations.push('environment_key', 'env', 'env_key'); } if (field === 'project') { variations.push('project_id', 'project_key'); } if (field === 'audience') { variations.push('audience_id', 'audience_key'); } // Add underscore variations if (!field.includes('_')) { variations.push(`${field}_id`, `${field}_key`, `${field}_name`); } return variations; } /** * Check junction tables for local field resolution */ checkJunctionTableLocal(field, context) { // flags + environment → flag_environments table if (context.primaryEntity === 'flags' && field === 'environment') { return { source: 'local', fieldPath: 'flag_environments.environment_key', joinRequired: false, estimatedCost: 2, confidence: 0.95, reasoning: 'Environment data available in flag_environments junction table' }; } // experiments + page → experiment_pages table if (context.primaryEntity === 'experiments' && field === 'page') { return { source: 'local', fieldPath: 'experiment_pages.page_id', joinRequired: false, estimatedCost: 2, confidence: 0.9, reasoning: 'Page data available in experiment_pages junction table' }; } return { source: 'local', fieldPath: field, joinRequired: false, estimatedCost: 1, confidence: 0.1, reasoning: 'No junction table pattern found' }; } /** * Get potential JOIN paths for a field */ getPotentialJoins(entity, field) { const joins = []; // Only return JOINs if absolutely necessary // Most fields should be resolved locally return joins; // Return empty array - prefer local resolution } /** * Check if table is a large lookup table that causes explosions */ isLargeLookupTable(tableName) { const largeTables = ['environments', 'projects', 'audiences', 'attributes']; return largeTables.includes(tableName); } /** * Initialize local field mappings for each entity */ initializeLocalFieldMaps() { this.localFieldMaps = new Map([ ['flags', [ 'id', 'key', 'name', 'description', 'created', 'last_modified', 'project_id', 'archived', 'enabled' ]], ['flag_environments', [ 'flag_key', 'environment_key', 'enabled', 'project_id' ]], ['experiments', [ 'id', 'name', 'description', 'status', 'created', 'last_modified', 'project_id', 'campaign_id', 'archived' ]], ['audiences', [ 'id', 'name', 'description', 'conditions', 'created', 'last_modified', 'project_id', 'archived' ]], ['features', [ 'id', 'key', 'name', 'description', 'created', 'last_modified', 'project_id', 'archived' ]], ['pages', [ 'id', 'name', 'key', 'conditions', 'activation_type', 'created', 'project_id', 'archived' ]] ]); logger.info(`Initialized local field mappings for ${this.localFieldMaps.size} entities`); } /** * Initialize dangerous JOINs that cause explosions */ initializeDangerousJoins() { this.dangerousJoins = new Set([ 'flags_environment', // flags to environments table 'flags_project', // flags to projects table 'experiments_audience', // experiments to audiences table 'flags_audience', // flags to audiences table 'experiments_project' // experiments to projects table ]); logger.info(`Initialized ${this.dangerousJoins.size} dangerous JOIN patterns`); } } /** * Factory function to create field resolution strategy based on query context */ export function createFieldResolutionStrategy(context) { // Aggregation queries need stricter JOIN prevention if (context.isAggregation || context.queryType === 'count') { return { checkLocal: true, requireExplicit: true, aggregationAware: true, cardinalityLimit: 2, // Max 2x multiplication for aggregations fallbackToSimple: true, timeoutLimit: 5000 // 5 second timeout }; } // Detail queries can be more permissive return { checkLocal: true, requireExplicit: false, aggregationAware: false, cardinalityLimit: 10, // Allow higher multiplication for detail fallbackToSimple: false, timeoutLimit: 10000 // 10 second timeout }; } //# sourceMappingURL=FieldResolutionStrategy.js.map