/** * Dedicated index for tracking soft-deleted items * This is MUCH more efficient than checking every item in the database * * Performance characteristics: * - Add deleted item: O(1) * - Remove deleted item: O(1) * - Check if deleted: O(1) * - Get all deleted: O(d) where d = number of deleted items << total items */ export class DeletedItemsIndex { private deletedIds: Set = new Set() private deletedCount: number = 0 /** * Mark an item as deleted */ markDeleted(id: string): void { if (!this.deletedIds.has(id)) { this.deletedIds.add(id) this.deletedCount++ } } /** * Mark an item as not deleted (restored) */ markRestored(id: string): void { if (this.deletedIds.delete(id)) { this.deletedCount-- } } /** * Check if an item is deleted - O(1) */ isDeleted(id: string): boolean { return this.deletedIds.has(id) } /** * Get all deleted item IDs - O(d) */ getAllDeleted(): string[] { return Array.from(this.deletedIds) } /** * Filter out deleted items from results - O(k) where k = result count */ filterDeleted(items: T[]): T[] { if (this.deletedCount === 0) { // Fast path - no deleted items return items } return items.filter(item => { const id = item.id return id ? !this.deletedIds.has(id) : true }) } /** * Get statistics */ getStats() { return { deletedCount: this.deletedCount, memoryUsage: this.deletedCount * 100 // Rough estimate: 100 bytes per ID } } /** * Clear all deleted items (for testing) */ clear(): void { this.deletedIds.clear() this.deletedCount = 0 } /** * Serialize for persistence */ serialize(): string { return JSON.stringify(Array.from(this.deletedIds)) } /** * Deserialize from persistence */ deserialize(data: string): void { try { const ids = JSON.parse(data) this.deletedIds = new Set(ids) this.deletedCount = this.deletedIds.size } catch (e) { console.warn('Failed to deserialize deleted items index') } } } /** * Global singleton for deleted items tracking */ export const deletedItemsIndex = new DeletedItemsIndex()