UNPKG

memory-engineering

Version:

Advanced Memory-Aware Code Context System with claude-flow-inspired architecture, showcasing MongoDB + Voyage AI strategic positioning

194 lines (159 loc) 5.8 kB
/** * MongoDB Backend Implementation * Following claude-flow-main proven patterns for robust memory storage */ import { MongoClient, Db, Collection } from 'mongodb'; import { IMemoryBackend, MemoryEntry, MemoryQuery, MemoryBackendConfig, MemoryBackendError } from './backend-interface.js'; export class MongoDBBackend implements IMemoryBackend { private client?: MongoClient; private db?: Db; private collection?: Collection<MemoryEntry>; private initialized = false; constructor(private config: MemoryBackendConfig) {} async initialize(): Promise<void> { if (this.initialized) return; try { this.client = new MongoClient(this.config.connectionString); await this.client.connect(); const dbName = this.config.databaseName || 'memory_engineering'; this.db = this.client.db(dbName); this.collection = this.db.collection(this.config.collectionName || 'memories'); if (this.config.enableIndexes !== false) { await this.createIndexes(); } this.initialized = true; } catch (error) { throw new MemoryBackendError('Failed to initialize MongoDB backend', { error }); } } async shutdown(): Promise<void> { if (this.client) { await this.client.close(); delete this.client; delete this.db; delete this.collection; } this.initialized = false; } async store(entry: MemoryEntry): Promise<void> { if (!this.collection) { throw new MemoryBackendError('Backend not initialized'); } try { // Use replaceOne for clean upsert - following claude-flow pattern const document = { ...entry, updatedAt: new Date() }; delete (document as any)._id; await this.collection.replaceOne( { id: entry.id }, document, { upsert: true } ); } catch (error) { throw new MemoryBackendError('Failed to store entry', { error, entryId: entry.id }); } } async retrieve(id: string): Promise<MemoryEntry | undefined> { if (!this.collection) { throw new MemoryBackendError('Backend not initialized'); } try { const entry = await this.collection.findOne({ id }); return entry || undefined; } catch (error) { throw new MemoryBackendError('Failed to retrieve entry', { error, entryId: id }); } } async update(_id: string, entry: MemoryEntry): Promise<void> { await this.store(entry); } async delete(id: string): Promise<void> { if (!this.collection) { throw new MemoryBackendError('Backend not initialized'); } try { await this.collection.deleteOne({ id }); } catch (error) { throw new MemoryBackendError('Failed to delete entry', { error, entryId: id }); } } async query(query: MemoryQuery): Promise<MemoryEntry[]> { if (!this.collection) { throw new MemoryBackendError('Backend not initialized'); } try { const filter: any = {}; if (query.projectPath) filter.projectPath = query.projectPath; if (query.memoryType) filter.memoryType = query.memoryType; if (query.tags && query.tags.length > 0) filter.tags = { $in: query.tags }; if (query.startTime || query.endTime) { filter.timestamp = {}; if (query.startTime) filter.timestamp.$gte = query.startTime; if (query.endTime) filter.timestamp.$lte = query.endTime; } if (query.search) { filter.$or = [ { searchableText: { $regex: query.search, $options: 'i' } }, { 'content.projectName': { $regex: query.search, $options: 'i' } }, { 'content.corePurpose': { $regex: query.search, $options: 'i' } } ]; } const cursor = this.collection.find(filter) .sort({ timestamp: -1 }) .limit(query.limit || 50); if (query.offset) cursor.skip(query.offset); return await cursor.toArray(); } catch (error) { throw new MemoryBackendError('Failed to query entries', { error, query }); } } async getAllEntries(): Promise<MemoryEntry[]> { if (!this.collection) { throw new MemoryBackendError('Backend not initialized'); } try { return await this.collection.find({}).toArray(); } catch (error) { throw new MemoryBackendError('Failed to get all entries', { error }); } } async getHealthStatus(): Promise<{ healthy: boolean; error?: string; metrics?: Record<string, number>; }> { if (!this.db || !this.collection) { return { healthy: false, error: 'Backend not initialized' }; } try { await this.db.admin().ping(); let metrics: Record<string, number> | undefined; if (this.config.enableMetrics !== false) { const count = await this.collection.countDocuments(); const stats = await this.db.stats(); metrics = { entryCount: count, dbSizeBytes: stats.dataSize || 0, indexSizeBytes: stats.indexSize || 0 }; } return { healthy: true, metrics }; } catch (error) { return { healthy: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } private async createIndexes(): Promise<void> { if (!this.collection) return; try { await this.collection.createIndex({ id: 1 }, { unique: true, background: true }); await this.collection.createIndex({ projectPath: 1, memoryType: 1 }, { background: true }); await this.collection.createIndex({ timestamp: -1 }, { background: true }); await this.collection.createIndex({ tags: 1 }, { background: true }); await this.collection.createIndex({ 'metadata.version': 1 }, { background: true }); } catch (error) { // Index creation errors are non-fatal } } }