@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
695 lines • 28.8 kB
JavaScript
/**
* IntelligentFieldMapper - Handles complex cross-table field resolution for the Dynamic JSON Query Engine
*
* This class solves the fundamental problem of mapping logical fields like "flags.environment"
* to actual database schema with proper JOINs and field references.
*/
import { getLogger } from '../logging/Logger.js';
export class IntelligentFieldMapper {
schemaMap;
constructor() {
this.schemaMap = this.initializeSchemaMap();
}
/**
* Resolve a logical field reference to actual database structure
*/
resolveField(fieldPath, primaryEntity, context = {}) {
getLogger().debug({
fieldPath,
primaryEntity,
contextFilters: context.filters?.length || 0
}, 'IntelligentFieldMapper: Resolving field path');
// Handle simple direct column references first
if (!fieldPath.includes('.')) {
return this.resolveDirectField(fieldPath, primaryEntity);
}
// Parse complex field path (e.g., "flags.environment", "experiments.variations.traffic")
const pathParts = fieldPath.split('.');
const [entityPart, fieldPart, ...nestedParts] = pathParts;
// Handle cross-table references
if (entityPart !== primaryEntity) {
// CRITICAL: Handle common entity reference patterns without explicit context
// AI agents often use plural entity names directly without specifying the parent entity
// Map of entity references that should be resolved based on context
const contextualEntityMappings = {
'variations': (pe, parts) => this.resolveVariationRelation(pe, parts),
'variation': (pe, parts) => this.resolveVariationRelation(pe, parts),
'audiences': (pe, parts) => this.resolveAudienceRelation(pe, parts),
'audience': (pe, parts) => this.resolveAudienceRelation(pe, parts),
'metrics': (pe, parts) => this.resolveMetricsRelation(pe, parts),
'metric': (pe, parts) => this.resolveMetricsRelation(pe, parts),
'pages': (pe, parts) => this.resolvePagesRelation(pe, parts),
'page': (pe, parts) => this.resolvePagesRelation(pe, parts),
'events': (pe, parts) => this.resolveEventsRelation(pe, parts),
'event': (pe, parts) => this.resolveEventsRelation(pe, parts),
'rules': (pe, parts) => this.resolveRulesRelation(pe, parts),
'rule': (pe, parts) => this.resolveRulesRelation(pe, parts),
'environments': (pe, parts) => this.resolveEnvironmentsRelation(pe, parts),
'environment': (pe, parts) => this.resolveEnvironmentsRelation(pe, parts),
'attributes': (pe, parts) => this.resolveAttributesRelation(pe, parts),
'attribute': (pe, parts) => this.resolveAttributesRelation(pe, parts),
'variables': (pe, parts) => this.resolveVariablesRelation(pe, parts),
'variable': (pe, parts) => this.resolveVariablesRelation(pe, parts),
'changes': (pe, parts) => this.resolveChangesRelation(pe, parts),
'whitelist': (pe, parts) => this.resolveWhitelistRelation(pe, parts),
'fields': (pe, parts) => this.resolveFieldsRelation(pe, parts),
};
// Check if this is a known entity reference pattern
const resolver = contextualEntityMappings[entityPart];
if (resolver) {
try {
const result = resolver(primaryEntity, [fieldPart, ...nestedParts].filter(Boolean));
if (result)
return result;
}
catch (e) {
// If resolver fails, fall through to generic resolution
getLogger().debug({
entityPart,
primaryEntity,
error: e instanceof Error ? e.message : String(e)
}, 'IntelligentFieldMapper: Contextual resolver failed, trying generic resolution');
}
}
return this.resolveCrossTableField(entityPart, fieldPart, primaryEntity, nestedParts);
}
// Handle nested field within primary entity
return this.resolveNestedField(fieldPart, primaryEntity, nestedParts);
}
/**
* Get all required JOINs for a set of field mappings
*/
getRequiredJoins(mappings) {
const joins = new Map();
for (const mapping of mappings) {
if (mapping.requiredJoin) {
const joinDef = this.getJoinDefinition(mapping.requiredJoin);
if (joinDef) {
joins.set(mapping.requiredJoin, joinDef);
}
}
}
return Array.from(joins.values());
}
/**
* Resolve direct field on primary table
*/
resolveDirectField(field, primaryEntity) {
const schema = this.schemaMap.get(primaryEntity);
if (!schema) {
throw new Error(`Unknown entity type: ${primaryEntity}`);
}
// CRITICAL FIX: Handle special cross-table fields that don't use dot notation
// These are fields that logically belong to the entity but are stored in related tables
if ((primaryEntity === 'flags' || primaryEntity === 'flag') && field === 'environment') {
return {
sqlField: 'flag_environments.environment_key',
requiredJoin: 'flag_environments',
additionalConditions: []
};
}
// Apply field name mappings
const mappedField = this.applyFieldMappings(field, primaryEntity);
if (mappedField) {
return {
sqlField: `${schema.table}.${mappedField}`
};
}
// Check if it's a valid column
if (schema.columns?.includes(field)) {
return {
sqlField: `${schema.table}.${field}`
};
}
// Check if it's a JSON field that needs processing
if (schema.jsonColumns?.includes(field)) {
return {
sqlField: `${schema.table}.${field}`,
requiresJsonProcessing: true
};
}
throw new Error(`Unknown field: ${field} on ${primaryEntity}`);
}
/**
* Resolve cross-table field references (e.g., flags.environment)
*/
resolveCrossTableField(targetEntity, field, primaryEntity, nestedParts) {
getLogger().debug({
targetEntity,
field,
primaryEntity,
nestedParts
}, 'IntelligentFieldMapper: Resolving cross-table field');
// Handle the specific case: flags.environment
if (primaryEntity === 'flags' && targetEntity === 'flags' && field === 'environment') {
return {
sqlField: 'flag_environments.environment_key',
requiredJoin: 'flag_environments',
additionalConditions: []
};
}
// Handle experiments.variations
if (primaryEntity === 'experiments' && targetEntity === 'experiments' && field === 'variations') {
// Web Experimentation: variations are in data_json
if (nestedParts.length > 0) {
// Handle nested access like experiments.variations.key
const nestedField = nestedParts.join('.');
return {
sqlField: 'data_json',
requiresJsonProcessing: true,
jsonataPath: `variations.${nestedField}`
};
}
// Count variations in JSON array
return {
sqlField: 'JSON_ARRAY_LENGTH(data_json, "$.variations")',
requiresJsonProcessing: true
};
}
// Handle flags.environments (plural - all environments)
if (primaryEntity === 'flags' && targetEntity === 'flags' && field === 'environments') {
if (nestedParts.length > 0) {
// Handle nested like flags.environments.production.enabled
const envName = nestedParts[0];
const envField = nestedParts.slice(1).join('.');
return {
sqlField: `flag_environments.${envField || 'enabled'}`,
requiredJoin: 'flag_environments',
additionalConditions: [`flag_environments.environment_key = '${envName}'`]
};
}
// Return environment data as JSON
return {
sqlField: 'flag_environments.data_json',
requiredJoin: 'flag_environments',
requiresJsonProcessing: true
};
}
// Handle audience relationships
if (field === 'audiences' || field === 'audience') {
return this.resolveAudienceRelation(primaryEntity, nestedParts);
}
// Handle variations relationships
if (field === 'variations' || field === 'variation') {
return this.resolveVariationRelation(primaryEntity, nestedParts);
}
// Generic entity-to-entity resolution
return this.resolveGenericRelation(targetEntity, field, primaryEntity, nestedParts);
}
/**
* Resolve nested field within primary entity's JSON data
*/
resolveNestedField(field, primaryEntity, nestedParts) {
const schema = this.schemaMap.get(primaryEntity);
if (!schema) {
throw new Error(`Unknown entity type: ${primaryEntity}`);
}
// Check if this is a JSON field access
if (schema.jsonColumns?.includes('data_json')) {
const fullPath = [field, ...nestedParts].join('.');
return {
sqlField: `${schema.table}.data_json`,
requiresJsonProcessing: true,
jsonataPath: `$.${fullPath}`
};
}
throw new Error(`Cannot resolve nested field: ${field} in ${primaryEntity}`);
}
/**
* Resolve audience-related field references
*/
resolveAudienceRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'experiments') {
if (nestedParts.length === 0) {
// Count audiences
return {
sqlField: 'COUNT(DISTINCT audiences.id)',
requiredJoin: 'experiment_audiences'
};
}
// Access audience field
const audienceField = nestedParts.join('.');
return {
sqlField: `audiences.${audienceField}`,
requiredJoin: 'experiment_audiences'
};
}
throw new Error(`Audience relation not supported for ${primaryEntity}`);
}
/**
* Resolve variation-related field references
*/
resolveVariationRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'flags') {
// Feature Experimentation: variations are in separate table
if (nestedParts.length === 0) {
// Count variations
return {
sqlField: 'COUNT(DISTINCT variations.key)',
requiredJoin: 'variations'
};
}
// Access variation field
const variationField = nestedParts.join('.');
if (variationField === 'traffic_allocation' || variationField === 'weight') {
return {
sqlField: 'variations.weight',
requiredJoin: 'variations'
};
}
return {
sqlField: `variations.${variationField}`,
requiredJoin: 'variations'
};
}
else if (primaryEntity === 'experiments') {
// Web Experimentation: variations are in data_json
if (nestedParts.length === 0) {
// Count variations in JSON array
return {
sqlField: 'JSON_ARRAY_LENGTH(data_json, "$.variations")',
requiresJsonProcessing: true
};
}
// Access variation field from JSON
const variationField = nestedParts.join('.');
return {
sqlField: 'data_json',
requiresJsonProcessing: true,
jsonataPath: `variations.${variationField}`
};
}
throw new Error(`Variation relation not supported for ${primaryEntity}`);
}
/**
* Resolve metrics-related field references
*/
resolveMetricsRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'experiments' || primaryEntity === 'campaigns') {
if (nestedParts.length === 0) {
// Count metrics
return {
sqlField: 'JSON_ARRAY_LENGTH(data_json, "$.metrics")',
requiresJsonProcessing: true
};
}
// Access specific metric field
const metricField = nestedParts.join('.');
return {
sqlField: 'data_json',
requiresJsonProcessing: true,
jsonataPath: `metrics.${metricField}`
};
}
throw new Error(`Metrics relation not supported for ${primaryEntity}`);
}
/**
* Resolve pages-related field references
*/
resolvePagesRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'experiments' || primaryEntity === 'campaigns') {
if (nestedParts.length === 0) {
// Count pages
return {
sqlField: 'JSON_ARRAY_LENGTH(data_json, "$.page_ids")',
requiresJsonProcessing: true
};
}
// For specific page access, would need JOIN to pages table
throw new Error(`Direct page field access not supported. Use page_ids array instead.`);
}
throw new Error(`Pages relation not supported for ${primaryEntity}`);
}
/**
* Resolve events-related field references
*/
resolveEventsRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'experiments') {
// Events are referenced through metrics
if (nestedParts.length === 0) {
return {
sqlField: 'JSON_ARRAY_LENGTH(data_json, "$.metrics")',
requiresJsonProcessing: true
};
}
// Access event through metrics
const eventField = nestedParts.join('.');
return {
sqlField: 'data_json',
requiresJsonProcessing: true,
jsonataPath: `metrics[*].event_id`
};
}
throw new Error(`Events relation not supported for ${primaryEntity}`);
}
/**
* Resolve rules-related field references
*/
resolveRulesRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'flags') {
// Rules are in a separate table
if (nestedParts.length === 0) {
return {
sqlField: 'COUNT(DISTINCT rules.id)',
requiredJoin: 'rules'
};
}
const ruleField = nestedParts.join('.');
return {
sqlField: `rules.${ruleField}`,
requiredJoin: 'rules'
};
}
throw new Error(`Rules relation not supported for ${primaryEntity}`);
}
/**
* Resolve environments-related field references
*/
resolveEnvironmentsRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'flags') {
if (nestedParts.length === 0) {
// Count environments
return {
sqlField: 'COUNT(DISTINCT flag_environments.environment_key)',
requiredJoin: 'flag_environments'
};
}
// Handle specific environment access like environments.production.enabled
if (nestedParts.length >= 2) {
const [envName, envField] = nestedParts;
return {
sqlField: `flag_environments.${envField || 'enabled'}`,
requiredJoin: 'flag_environments',
additionalConditions: [`flag_environments.environment_key = '${envName}'`]
};
}
// General environment field access
const envField = nestedParts.join('.');
return {
sqlField: `flag_environments.${envField}`,
requiredJoin: 'flag_environments'
};
}
else if (primaryEntity === 'experiments') {
// Experiments have environments in JSON
if (nestedParts.length === 0) {
return {
sqlField: 'data_json',
requiresJsonProcessing: true,
jsonataPath: 'environments'
};
}
const envPath = nestedParts.join('.');
return {
sqlField: 'data_json',
requiresJsonProcessing: true,
jsonataPath: `environments.${envPath}`
};
}
throw new Error(`Environments relation not supported for ${primaryEntity}`);
}
/**
* Resolve attributes-related field references
*/
resolveAttributesRelation(primaryEntity, nestedParts) {
// Attributes are typically referenced through audience conditions
if (primaryEntity === 'audiences') {
if (nestedParts.length === 0) {
// Can't directly count attributes from conditions JSON
throw new Error(`Direct attribute counting not supported. Attributes are embedded in audience conditions.`);
}
// Attributes are in the conditions JSON
return {
sqlField: 'audiences.conditions',
requiresJsonProcessing: true,
jsonataPath: `$[?(@.type == "custom_attribute")]`
};
}
throw new Error(`Attributes relation not supported for ${primaryEntity}`);
}
/**
* Resolve variables-related field references
*/
resolveVariablesRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'flags') {
if (nestedParts.length === 0) {
// Count variable definitions
return {
sqlField: 'JSON_ARRAY_LENGTH(flags.data_json, "$.variable_definitions")',
requiresJsonProcessing: true
};
}
// Access variable definition field
const varField = nestedParts.join('.');
return {
sqlField: 'flags.data_json',
requiresJsonProcessing: true,
jsonataPath: `variable_definitions.${varField}`
};
}
throw new Error(`Variables relation not supported for ${primaryEntity}`);
}
/**
* Resolve changes-related field references (array field in experiments/campaigns)
*/
resolveChangesRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'experiments' || primaryEntity === 'campaigns') {
if (nestedParts.length === 0) {
return {
sqlField: 'JSON_ARRAY_LENGTH(data_json, "$.changes")',
requiresJsonProcessing: true
};
}
const changeField = nestedParts.join('.');
return {
sqlField: 'data_json',
requiresJsonProcessing: true,
jsonataPath: `changes.${changeField}`
};
}
throw new Error(`Changes relation not supported for ${primaryEntity}`);
}
/**
* Resolve whitelist-related field references
*/
resolveWhitelistRelation(primaryEntity, nestedParts) {
if (primaryEntity === 'experiments') {
if (nestedParts.length === 0) {
return {
sqlField: 'JSON_ARRAY_LENGTH(data_json, "$.whitelist")',
requiresJsonProcessing: true
};
}
const whitelistField = nestedParts.join('.');
return {
sqlField: 'data_json',
requiresJsonProcessing: true,
jsonataPath: `whitelist.${whitelistField}`
};
}
throw new Error(`Whitelist relation not supported for ${primaryEntity}`);
}
/**
* Resolve fields-related field references (extension fields)
*/
resolveFieldsRelation(primaryEntity, nestedParts) {
// Extensions entity type is not supported in the current analytics engine
// This resolver is here for future expansion when extensions are added
throw new Error(`Fields relation not supported for ${primaryEntity}. Extensions entity type is not yet implemented.`);
}
/**
* Resolve generic entity relationships
*/
resolveGenericRelation(targetEntity, field, primaryEntity, nestedParts) {
// This would be expanded to handle other cross-table relationships
// For now, throw an error for unsupported relations
throw new Error(`Cross-table relation not supported: ${targetEntity}.${field} from ${primaryEntity}`);
}
/**
* Get JOIN definition for a required join
*/
getJoinDefinition(joinName) {
const joinDefinitions = {
'flag_environments': {
alias: 'flag_environments',
table: 'flag_environments',
condition: 'flags.project_id = flag_environments.project_id AND flags.key = flag_environments.flag_key',
type: 'LEFT'
},
'variations': {
alias: 'variations',
table: 'variations',
condition: 'flags.project_id = variations.project_id AND flags.key = variations.flag_key',
type: 'LEFT'
},
'experiment_audiences': {
alias: 'audiences',
table: 'audiences',
condition: 'EXISTS (SELECT 1 FROM json_each(experiments.data_json, "$.audience_ids") WHERE value = audiences.id)',
type: 'LEFT'
},
'rules': {
alias: 'rules',
table: 'rules',
condition: 'flags.project_id = rules.project_id AND flags.key = rules.flag_key',
type: 'LEFT'
},
'pages': {
alias: 'pages',
table: 'pages',
condition: 'pages.project_id = experiments.project_id AND EXISTS (SELECT 1 FROM json_each(experiments.data_json, "$.page_ids") WHERE value = pages.id)',
type: 'LEFT'
},
'events': {
alias: 'events',
table: 'events',
condition: 'events.project_id = experiments.project_id AND EXISTS (SELECT 1 FROM json_each(experiments.data_json, "$.metrics") m, json_each(m.value, "$.event_id") WHERE value = events.id)',
type: 'LEFT'
}
};
return joinDefinitions[joinName] || null;
}
/**
* Apply field name mappings for common aliases
*/
applyFieldMappings(field, primaryEntity) {
const commonMappings = {
'created': 'created_time',
'updated': 'updated_time',
'modified': 'updated_time',
'last_modified': 'updated_time',
'is_archived': 'archived',
'enabled': 'archived', // Inverse logic
'active': 'archived' // Inverse logic
};
// Entity-specific mappings (partial mapping for supported entities)
const entityMappings = {
'flags': {
'flag_key': 'key',
'flag_name': 'name'
},
'flag': {
'flag_key': 'key',
'flag_name': 'name'
},
'experiments': {
'experiment_id': 'id',
'experiment_name': 'name'
},
'experiment': {
'experiment_id': 'id',
'experiment_name': 'name'
},
'audiences': {
'audience_id': 'id',
'audience_name': 'name'
},
'audience': {
'audience_id': 'id',
'audience_name': 'name'
},
'campaigns': {
'campaign_id': 'id',
'campaign_name': 'name'
},
'campaign': {
'campaign_id': 'id',
'campaign_name': 'name'
},
'pages': {
'page_id': 'id',
'page_name': 'name'
},
'page': {
'page_id': 'id',
'page_name': 'name'
},
'projects': {
'project_name': 'name'
},
'project': {
'project_name': 'name'
}
};
// Check entity-specific mappings first
const entitySpecific = entityMappings[primaryEntity];
if (entitySpecific && entitySpecific[field]) {
return entitySpecific[field];
}
// Check common mappings
return commonMappings[field] || null;
}
/**
* Initialize the schema map with current database structure
*/
initializeSchemaMap() {
const schemaMap = new Map();
// Flags schema
schemaMap.set('flags', {
table: 'flags',
columns: ['project_id', 'key', 'id', 'name', 'description', 'archived', 'created_time', 'updated_time', 'data_json'],
jsonColumns: ['data_json'],
joins: {
environments: {
table: 'flag_environments',
on: 'flags.project_id = flag_environments.project_id AND flags.key = flag_environments.flag_key',
type: 'LEFT'
},
variations: {
table: 'variations',
on: 'flags.project_id = variations.project_id AND flags.key = variations.flag_key',
type: 'LEFT'
},
rules: {
table: 'rules',
on: 'flags.project_id = rules.project_id AND flags.key = rules.flag_key',
type: 'LEFT'
},
rulesets: {
table: 'rulesets',
on: 'flags.project_id = rulesets.project_id AND flags.key = rulesets.flag_key',
type: 'LEFT'
}
}
});
// Experiments schema
schemaMap.set('experiments', {
table: 'experiments',
columns: ['id', 'project_id', 'name', 'description', 'status', 'type', 'flag_key', 'environment', 'archived', 'created_time', 'updated_time', 'data_json'],
jsonColumns: ['data_json'],
joins: {
results: {
table: 'experiment_results',
on: 'experiments.id = experiment_results.experiment_id',
type: 'LEFT'
},
campaigns: {
table: 'campaigns',
on: 'JSON_EXTRACT(experiments.data_json, "$.campaign_id") = campaigns.id',
type: 'LEFT'
},
audiences: {
table: 'audiences',
on: 'EXISTS (SELECT 1 FROM json_each(experiments.data_json, "$.audience_ids") WHERE value = audiences.id)',
type: 'LEFT'
}
}
});
// Audiences schema
schemaMap.set('audiences', {
table: 'audiences',
columns: ['id', 'project_id', 'name', 'description', 'conditions', 'archived', 'created_time', 'last_modified', 'data_json'],
jsonColumns: ['conditions', 'data_json'],
joins: {
experiments: {
table: 'experiments',
on: 'EXISTS (SELECT 1 FROM json_each(experiments.data_json, "$.audience_ids") WHERE value = audiences.id)',
type: 'LEFT'
}
}
});
// Add other entity schemas as needed...
return schemaMap;
}
}
//# sourceMappingURL=IntelligentFieldMapper-INCOMPLETE.js.map