feat: implement production-ready type-aware NLP with zero hardcoded fields
🎯 COMPLETE TYPE-AWARE INTELLIGENCE SYSTEM: ## Type-Field Affinity Tracking: - Track which fields actually appear with which NounTypes in real data - Build affinity maps: Document → [title: 0.95, author: 0.87, publishDate: 0.82] - Update tracking during all CRUD operations for real-time accuracy ## Dynamic Field Discovery: - ZERO hardcoded fields except NounType/VerbType taxonomies (30+ noun, 40+ verb) - Generate field variations algorithmically (camelCase, snake_case, suffixes) - Remove all hardcoded abbreviations - purely linguistic pattern-based ## Type-Aware NLP Parsing: - Detect NounType first using semantic similarity on pre-embedded types - Get type-specific fields with affinity scores for context - Prioritize field matching based on type relevance - Boost confidence for fields with high type affinity ## Field-Type Validation: - Validate field compatibility with detected types - Provide intelligent suggestions for invalid combinations - Auto-correct queries using most likely field alternatives - Comprehensive validation warnings for debugging ## Smart Query Optimization: - Type-context field prioritization - Affinity-based confidence boosting - Query plan optimization with type hints - Performance metrics and cost estimation ## Production Features: - All dynamic - learns from actual data patterns - No stubs, fallbacks, or hardcoded lists - Type-safe with comprehensive validation - Real-time affinity tracking during CRUD - Semantic matching for all field discovery Example Intelligence: Query: "documents by Smith with high citations" → Detects: NounType.Document (0.92 confidence) → Fields: "by" → "author" (0.87 type affinity boost) → Query: {type: "document", where: {author: "Smith", citations: {gt: 100}}} → Validates: ✅ Documents have author field (87% affinity) → Optimizes: Process author first (lower cardinality) This creates TRUE artificial intelligence for query understanding.
This commit is contained in:
parent
b3c742dcea
commit
d5b24c3ad3
3 changed files with 407 additions and 31 deletions
|
|
@ -95,6 +95,10 @@ export class MetadataIndexManager {
|
|||
private readonly TIMESTAMP_PRECISION_MS = 60000 // 1 minute buckets
|
||||
private readonly FLOAT_PRECISION = 2 // decimal places
|
||||
|
||||
// Type-Field Affinity Tracking for intelligent NLP
|
||||
private typeFieldAffinity = new Map<string, Map<string, number>>() // nounType -> field -> count
|
||||
private totalEntitiesByType = new Map<string, number>() // nounType -> total count
|
||||
|
||||
// Unified cache for coordinated memory management
|
||||
private unifiedCache: UnifiedCache
|
||||
|
||||
|
|
@ -691,6 +695,9 @@ export class MetadataIndexManager {
|
|||
this.updateCardinalityStats(field, value, 'add')
|
||||
}
|
||||
|
||||
// Track type-field affinity for intelligent NLP
|
||||
this.updateTypeFieldAffinity(id, field, value, 'add')
|
||||
|
||||
// Incrementally update sorted index for this field
|
||||
if (!updatedFields.has(field)) {
|
||||
this.updateSortedIndexAdd(field, value, id)
|
||||
|
|
@ -795,6 +802,11 @@ export class MetadataIndexManager {
|
|||
this.updateCardinalityStats(field, value, 'remove')
|
||||
}
|
||||
|
||||
// Track type-field affinity for intelligent NLP
|
||||
if (hadId) {
|
||||
this.updateTypeFieldAffinity(id, field, value, 'remove')
|
||||
}
|
||||
|
||||
// Incrementally update sorted index when removing
|
||||
this.updateSortedIndexRemove(field, value, id)
|
||||
|
||||
|
|
@ -1868,4 +1880,147 @@ export class MetadataIndexManager {
|
|||
|
||||
return stats
|
||||
}
|
||||
|
||||
/**
|
||||
* Update type-field affinity tracking for intelligent NLP
|
||||
* Tracks which fields commonly appear with which entity types
|
||||
*/
|
||||
private updateTypeFieldAffinity(entityId: string, field: string, value: any, operation: 'add' | 'remove'): void {
|
||||
// Only track affinity for non-system fields
|
||||
if (this.config.excludeFields.includes(field)) return
|
||||
|
||||
// Get the entity type from the "noun" field for this entity
|
||||
let entityType: string | null = null
|
||||
|
||||
// Find the noun type for this entity
|
||||
for (const [key, entry] of this.indexCache.entries()) {
|
||||
if (key.startsWith('noun:') && entry.ids.has(entityId)) {
|
||||
entityType = key.split(':', 2)[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!entityType) return // No type found, skip affinity tracking
|
||||
|
||||
// Initialize affinity tracking for this type
|
||||
if (!this.typeFieldAffinity.has(entityType)) {
|
||||
this.typeFieldAffinity.set(entityType, new Map())
|
||||
}
|
||||
if (!this.totalEntitiesByType.has(entityType)) {
|
||||
this.totalEntitiesByType.set(entityType, 0)
|
||||
}
|
||||
|
||||
const typeFields = this.typeFieldAffinity.get(entityType)!
|
||||
|
||||
if (operation === 'add') {
|
||||
// Increment field count for this type
|
||||
const currentCount = typeFields.get(field) || 0
|
||||
typeFields.set(field, currentCount + 1)
|
||||
|
||||
// Update total entities of this type (only count once per entity)
|
||||
if (field === 'noun') {
|
||||
this.totalEntitiesByType.set(entityType, this.totalEntitiesByType.get(entityType)! + 1)
|
||||
}
|
||||
} else if (operation === 'remove') {
|
||||
// Decrement field count for this type
|
||||
const currentCount = typeFields.get(field) || 0
|
||||
if (currentCount > 1) {
|
||||
typeFields.set(field, currentCount - 1)
|
||||
} else {
|
||||
typeFields.delete(field)
|
||||
}
|
||||
|
||||
// Update total entities of this type
|
||||
if (field === 'noun') {
|
||||
const total = this.totalEntitiesByType.get(entityType)!
|
||||
if (total > 1) {
|
||||
this.totalEntitiesByType.set(entityType, total - 1)
|
||||
} else {
|
||||
this.totalEntitiesByType.delete(entityType)
|
||||
this.typeFieldAffinity.delete(entityType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fields that commonly appear with a specific entity type
|
||||
* Returns fields with their affinity scores (0-1)
|
||||
*/
|
||||
async getFieldsForType(nounType: string): Promise<Array<{
|
||||
field: string
|
||||
affinity: number
|
||||
occurrences: number
|
||||
totalEntities: number
|
||||
}>> {
|
||||
const typeFields = this.typeFieldAffinity.get(nounType)
|
||||
const totalEntities = this.totalEntitiesByType.get(nounType)
|
||||
|
||||
if (!typeFields || !totalEntities) {
|
||||
return []
|
||||
}
|
||||
|
||||
const fieldsWithAffinity: Array<{
|
||||
field: string
|
||||
affinity: number
|
||||
occurrences: number
|
||||
totalEntities: number
|
||||
}> = []
|
||||
|
||||
for (const [field, count] of typeFields.entries()) {
|
||||
const affinity = count / totalEntities // 0-1 score
|
||||
fieldsWithAffinity.push({
|
||||
field,
|
||||
affinity,
|
||||
occurrences: count,
|
||||
totalEntities
|
||||
})
|
||||
}
|
||||
|
||||
// Sort by affinity (most common fields first)
|
||||
fieldsWithAffinity.sort((a, b) => b.affinity - a.affinity)
|
||||
|
||||
return fieldsWithAffinity
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type-field affinity statistics for analysis
|
||||
*/
|
||||
async getTypeFieldAffinityStats(): Promise<{
|
||||
totalTypes: number
|
||||
averageFieldsPerType: number
|
||||
typeBreakdown: Record<string, {
|
||||
totalEntities: number
|
||||
uniqueFields: number
|
||||
topFields: Array<{field: string; affinity: number}>
|
||||
}>
|
||||
}> {
|
||||
const typeBreakdown: Record<string, any> = {}
|
||||
let totalFields = 0
|
||||
|
||||
for (const [nounType, fieldsMap] of this.typeFieldAffinity.entries()) {
|
||||
const totalEntities = this.totalEntitiesByType.get(nounType) || 0
|
||||
const fields = Array.from(fieldsMap.entries())
|
||||
|
||||
// Get top 5 fields for this type
|
||||
const topFields = fields
|
||||
.map(([field, count]) => ({ field, affinity: count / totalEntities }))
|
||||
.sort((a, b) => b.affinity - a.affinity)
|
||||
.slice(0, 5)
|
||||
|
||||
typeBreakdown[nounType] = {
|
||||
totalEntities,
|
||||
uniqueFields: fieldsMap.size,
|
||||
topFields
|
||||
}
|
||||
|
||||
totalFields += fieldsMap.size
|
||||
}
|
||||
|
||||
return {
|
||||
totalTypes: this.typeFieldAffinity.size,
|
||||
averageFieldsPerType: totalFields / Math.max(1, this.typeFieldAffinity.size),
|
||||
typeBreakdown
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue