import { StoreType, StoreOptions, PaginatedResult, QueryOptions, ListOptions } from '../types'; import { AbstractStore } from './abstractStore'; import { prepareDocument } from './baseStore'; import { DBError, ErrorCode } from '../core/error'; /** * DocStore implementation * Uses OrbitDB's document store which allows for more complex document storage with indices */ export class DocStore extends AbstractStore { constructor() { super(StoreType.DOCSTORE); } protected getLoggerName(): string { return 'DOCSTORE'; } /** * Prepare a document for creation - override to add _id which is required for docstore */ protected prepareCreateDocument>( collection: string, id: string, data: Omit, ): any { return { _id: id, ...prepareDocument(collection, data), }; } /** * Prepare a document for update - override to add _id which is required for docstore */ protected prepareUpdateDocument>( collection: string, id: string, data: Partial>, existing?: T, ): any { return { _id: id, ...prepareDocument( collection, data as unknown as Omit, existing, ), }; } /** * Implementation for the DocStore create operation */ protected async performCreate(db: any, id: string, document: any): Promise { return await db.put(document); } /** * Implementation for the DocStore get operation */ protected async performGet(db: any, id: string): Promise { return (await db.get(id)) as T | null; } /** * Implementation for the DocStore update operation */ protected async performUpdate(db: any, id: string, document: any): Promise { return await db.put(document); } /** * Implementation for the DocStore remove operation */ protected async performRemove(db: any, id: string): Promise { await db.del(id); } /** * List all documents in a collection with pagination */ async list>( collection: string, options?: ListOptions, ): Promise> { try { const db = await this.openStore(collection, options); const allDocs = await db.query((_doc: any) => true); // Map the documents to include id let documents = allDocs.map((doc: any) => ({ id: doc._id, ...doc, })) as T[]; // Apply sorting documents = this.applySorting(documents, options); // Apply pagination return this.applyPagination(documents, options); } catch (error) { this.handleError(`Error listing documents in ${collection}`, error); } } /** * Query documents in a collection with filtering and pagination */ async query>( collection: string, filter: (doc: T) => boolean, options?: QueryOptions, ): Promise> { try { const db = await this.openStore(collection, options); // Apply filter using docstore's query capability const filtered = await db.query((doc: any) => filter(doc as T)); // Map the documents to include id let documents = filtered.map((doc: any) => ({ id: doc._id, ...doc, })) as T[]; // Apply sorting documents = this.applySorting(documents, options); // Apply pagination return this.applyPagination(documents, options); } catch (error) { this.handleError(`Error querying documents in ${collection}`, error); } } /** * Create an index for a collection to speed up queries * DocStore has built-in indexing capabilities */ async createIndex(collection: string, field: string, options?: StoreOptions): Promise { try { const db = await this.openStore(collection, options); // DocStore supports indexing, so we create the index if (typeof db.createIndex === 'function') { await db.createIndex(field); this.logger.info(`Index created on ${field} for collection ${collection}`); return true; } this.logger.info( `Index creation not supported for this DB instance, but DocStore has built-in indices`, ); return true; } catch (error) { this.handleError(`Error creating index for ${collection}`, error); } } /** * Helper to open a store of the correct type */ private async openStore(collection: string, options?: StoreOptions): Promise { const { openStore } = await import('./baseStore'); return await openStore(collection, this.storeType, options); } /** * Helper to handle errors consistently */ private handleError(message: string, error: any): never { if (error instanceof DBError) { throw error; } this.logger.error(`${message}:`, error); throw new DBError(ErrorCode.OPERATION_FAILED, `${message}: ${error.message}`, error); } }