Skip to main content
Glama
index-manager.ts8.86 kB
/** * Database Index Management */ import { Session } from 'neo4j-driver'; export class IndexManager { constructor( private session: Session, private vectorDimensions?: number ) {} /** * Zero-fallback system database validation */ private async validateUserDatabase(): Promise<void> { try { // Query database information to validate context const result = await this.session.run('CALL db.info() YIELD name'); const dbName = result.records[0]?.get('name'); if (dbName === 'system') { throw new Error(`Cannot create constraints on system database. Context: ${dbName}`); } // Additional validation for null/undefined database name if (!dbName) { throw new Error('Database name could not be determined'); } } catch (error) { // ZERO-FALLBACK: Re-throw all errors to prevent masking real problems if (error instanceof Error) { throw error; } // For unknown error types, wrap and throw throw new Error(`Database validation failed: ${String(error)}`); } } /** * Ensure all required constraints exist */ async ensureConstraints(): Promise<void> { // ZERO-FALLBACK: Fail fast if wrong database context await this.validateUserDatabase(); const constraints = [ // CRITICAL: Used in all Memory CRUD operations 'CREATE CONSTRAINT memory_id_unique IF NOT EXISTS FOR (m:Memory) REQUIRE m.id IS UNIQUE', // CRITICAL: Used in observation deletion operations 'CREATE CONSTRAINT observation_id_unique IF NOT EXISTS FOR (o:Observation) REQUIRE o.id IS UNIQUE' ]; for (const constraint of constraints) { try { await this.session.run(constraint); } catch (error) { // Constraint creation failed - this IS a real problem throw error; } } } /** * Ensure ACTIVE indexes exist (verified query usage) */ async ensureActiveIndexes(): Promise<void> { // ZERO-FALLBACK: Fail fast if wrong database context await this.validateUserDatabase(); const activeIndexes = [ // VERIFIED USAGE: wildcard-search-service.ts, vector-search-channel.ts // Pattern: WHERE m.memoryType IN $memoryTypes 'CREATE INDEX memory_type_idx IF NOT EXISTS FOR (m:Memory) ON (m.memoryType)', // VERIFIED USAGE: All search result ordering // Pattern: ORDER BY m.createdAt DESC 'CREATE INDEX memory_created_idx IF NOT EXISTS FOR (m:Memory) ON (m.createdAt)', // NEW: Enhanced relationship metadata support // Pattern: WHERE r.strength >= $threshold 'CREATE INDEX relation_strength_idx IF NOT EXISTS FOR ()-[r:RELATES_TO]-() ON (r.strength)', // Pattern: WHERE r.source = "agent" 'CREATE INDEX relation_source_idx IF NOT EXISTS FOR ()-[r:RELATES_TO]-() ON (r.source)', // Pattern: ORDER BY r.createdAt DESC 'CREATE INDEX relation_created_idx IF NOT EXISTS FOR ()-[r:RELATES_TO]-() ON (r.createdAt)' ]; for (const index of activeIndexes) { try { await this.session.run(index); } catch (error) { // Index creation failed - real problem throw error; } } } /** * Ensure fulltext indexes exist */ async ensureFulltextIndexes(): Promise<void> { // ZERO-FALLBACK: Fail fast if wrong database context await this.validateUserDatabase(); const fulltextIndexes = [ // USAGE: CALL db.index.fulltext.queryNodes('memory_metadata_idx', $query) 'CREATE FULLTEXT INDEX memory_metadata_idx IF NOT EXISTS FOR (m:Memory) ON EACH [m.metadata]', // USAGE: CALL db.index.fulltext.queryNodes('observation_content_idx', $query) 'CREATE FULLTEXT INDEX observation_content_idx IF NOT EXISTS FOR (o:Observation) ON EACH [o.content]' ]; for (const index of fulltextIndexes) { try { await this.session.run(index); } catch (error) { // Fulltext index creation failed - real problem, no console output throw new Error(`Fulltext index failed: ${index} - ${error instanceof Error ? error.message : String(error)}`); } } } /** * Ensure vector indexes exist (GDS Plugin) */ async ensureVectorIndexes(): Promise<void> { // ZERO-FALLBACK: Fail fast if wrong database context await this.validateUserDatabase(); // Skip vector index creation if dimensions not provided if (!this.vectorDimensions) { return; } try { const dimensions = this.vectorDimensions; // CRITICAL VALIDATION: Ensure dimensions are valid before creating indexes if (!dimensions || dimensions <= 0) { throw new Error(`INVALID VECTOR DIMENSIONS: ${dimensions}. Model may not be loaded properly. Check embedding service configuration.`); } // VERIFIED USAGE: gds.similarity.cosine(m.nameEmbedding, $queryVector) const memoryVectorIndex = ` CREATE VECTOR INDEX memory_name_vector_idx IF NOT EXISTS FOR (m:Memory) ON (m.nameEmbedding) OPTIONS {indexConfig: { \`vector.dimensions\`: ${dimensions}, \`vector.similarity_function\`: 'cosine' }} `; await this.session.run(memoryVectorIndex); // POTENTIAL USAGE: Future observation-level vector search const observationVectorIndex = ` CREATE VECTOR INDEX observation_embedding_vector_idx IF NOT EXISTS FOR (o:Observation) ON (o.embedding) OPTIONS {indexConfig: { \`vector.dimensions\`: ${dimensions}, \`vector.similarity_function\`: 'cosine' }} `; await this.session.run(observationVectorIndex); } catch (error) { // Expected for Community Edition - silent operation } } /** * Remove dead indexes from legacy implementations */ async removeDeadIndexes(): Promise<void> { const deadIndexes = [ 'memory_accessed_idx', // No queries sort by lastAccessed 'memory_name_idx', // Replaced by fulltext and vector search 'relation_type_idx' // No queries filter by relationType alone ]; for (const indexName of deadIndexes) { try { await this.session.run(`DROP INDEX ${indexName} IF EXISTS`); // Silent cleanup - no logging noise for normal operations } catch (error) { // Index might not exist - continue cleanup silently } } } /** * Check if required schema elements exist */ async hasRequiredSchema(): Promise<boolean> { try { // Check for critical Memory.id unique constraint const memoryConstraintResult = await this.session.run(` SHOW CONSTRAINTS WHERE entityType = "NODE" AND labelsOrTypes = ["Memory"] AND properties = ["id"] AND type = "UNIQUENESS" `); // Check for critical Observation.id unique constraint const observationConstraintResult = await this.session.run(` SHOW CONSTRAINTS WHERE entityType = "NODE" AND labelsOrTypes = ["Observation"] AND properties = ["id"] AND type = "UNIQUENESS" `); // Check for active memory_type index const memoryTypeIndexResult = await this.session.run(` SHOW INDEXES WHERE name = "memory_type_idx" `); // Check for new memory_created index const memoryCreatedIndexResult = await this.session.run(` SHOW INDEXES WHERE name = "memory_created_idx" `); const hasRequiredElements = memoryConstraintResult.records.length > 0 && observationConstraintResult.records.length > 0 && memoryTypeIndexResult.records.length > 0 && memoryCreatedIndexResult.records.length > 0; return hasRequiredElements; } catch (error) { // Schema check failed - assume missing (silent) return false; } } /** * Initialize all database schema elements */ async initializeSchema(): Promise<void> { // ZERO-FALLBACK: Validate database context before any schema operations await this.validateUserDatabase(); try { // Step 1: Remove dead indexes first (clean slate) await this.removeDeadIndexes(); // Step 2: Create essential constraints await this.ensureConstraints(); // Step 3: Create active indexes (verified usage) await this.ensureActiveIndexes(); // Step 4: Create fulltext indexes await this.ensureFulltextIndexes(); // Step 5: Create vector indexes (if supported) await this.ensureVectorIndexes(); // Silent initialization - only real failures matter } catch (error) { // Schema initialization failed - this is a real problem throw error; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sylweriusz/mcp-neo4j-memory-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server