UNPKG

@decaf-ts/for-couchdb

Version:
174 lines 7.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CouchDBPaginator = void 0; const core_1 = require("@decaf-ts/core"); const db_decorators_1 = require("@decaf-ts/db-decorators"); const decorator_validation_1 = require("@decaf-ts/decorator-validation"); const decoration_1 = require("@decaf-ts/decoration"); /** * @description Paginator for ConuchDB query results * @summary Implements pagination for CouchDB queries using bookmarks for efficient navigation through result sets * @template M - The model type that extends Model * @template R - The result type * @param {CouchDBAdapter<any, any, any>} adapter - The CouchDB adapter * @param {MangoQuery} query - The Mango query to paginate * @param {number} size - The page size * @param {Constructor<M>} clazz - The model constructor * @class CouchDBPaginator * @example * // Example of using CouchDBPaginator * const adapter = new MyCouchDBAdapter(scope); * const query = { selector: { type: "user" } }; * const paginator = new CouchDBPaginator(adapter, query, 10, User); * * // Get the first page * const page1 = await paginator.page(1); * * // Get the next page * const page2 = await paginator.page(2); */ class CouchDBPaginator extends core_1.Paginator { /** * @description Creates a new CouchDBPaginator instance * @summary Initializes a paginator for CouchDB query results * @param {CouchDBAdapter<any, any, any, any>} adapter - The CouchDB adapter * @param {MangoQuery} query - The Mango query to paginate * @param {number} size - The page size * @param {Constructor<M>} clazz - The model constructor */ constructor(adapter, query, size, clazz) { super(adapter, query, size, clazz); this._bookmarks = new Map(); } isValidBookmark(value) { return typeof value === "string" && value.length > 0; } /** * @description Prepares a query for pagination * @summary Modifies the raw query to include pagination parameters * @param {MangoQuery} rawStatement - The original Mango query * @return {MangoQuery} The prepared query with pagination parameters */ prepare(rawStatement) { const query = Object.assign({}, rawStatement); if (query.limit) this.limit = query.limit; query.limit = this.size; delete query.skip; return query; } /** * @description Retrieves a specific page of results * @summary Executes the query with pagination and processes the results * @param {number} [page=1] - The page number to retrieve * @return {Promise<R[]>} A promise that resolves to an array of results * @throws {PagingError} If trying to access a page other than the first without a bookmark, or if no class is defined * @mermaid * sequenceDiagram * participant Client * participant CouchDBPaginator * participant Adapter * participant CouchDB * * Client->>CouchDBPaginator: page(pageNumber) * Note over CouchDBPaginator: Clone statement * CouchDBPaginator->>CouchDBPaginator: validatePage(page) * * alt page !== 1 * CouchDBPaginator->>CouchDBPaginator: Check bookmark * alt No bookmark * CouchDBPaginator-->>Client: Throw PagingError * else Has bookmark * CouchDBPaginator->>CouchDBPaginator: Add bookmark to statement * end * end * * CouchDBPaginator->>Adapter: raw(statement, false) * Adapter->>CouchDB: Execute query * CouchDB-->>Adapter: Return results * Adapter-->>CouchDBPaginator: Return MangoResponse * * Note over CouchDBPaginator: Process results * * alt Has warning * CouchDBPaginator->>CouchDBPaginator: Log warning * end * * CouchDBPaginator->>CouchDBPaginator: Check for clazz * * alt No clazz * CouchDBPaginator-->>Client: Throw PagingError * else Has clazz * CouchDBPaginator->>CouchDBPaginator: Find primary key * * alt Has fields in statement * CouchDBPaginator->>CouchDBPaginator: Use docs directly * else No fields * CouchDBPaginator->>CouchDBPaginator: Process each document * loop For each document * CouchDBPaginator->>CouchDBPaginator: Extract original ID * CouchDBPaginator->>Adapter: revert(doc, clazz, pkDef.id, parsedId) * end * end * * CouchDBPaginator->>CouchDBPaginator: Store bookmark * CouchDBPaginator->>CouchDBPaginator: Update currentPage * CouchDBPaginator-->>Client: Return results * end */ async page(page = 1, bookmark, ...args) { const { log, ctxArgs, ctx } = this.adapter["logCtx"]([bookmark, ...args], this.page); if (this.isPreparedStatement()) return await this.pagePrepared(page, ...ctxArgs); page = this.validatePage(page ?? 1); const statement = Object.assign({}, this.statement); statement.limit = this.size; if (page === 1) { if (this.isValidBookmark(bookmark)) { statement.bookmark = bookmark; } else { delete statement.bookmark; } } else { const explicitBookmark = bookmark || this._bookmarks.get(page); if (!this.isValidBookmark(explicitBookmark)) { throw new core_1.PagingError("Random page access requires a cached CouchDB bookmark. Start at page 1 and page forward, or pass an explicit bookmark."); } delete statement.skip; statement.bookmark = explicitBookmark; } delete statement.skip; const rawResult = (await this.adapter.raw(statement, false, ...ctxArgs)); const { docs, bookmark: nextBookmark, warning } = rawResult; if (warning) log.warn(warning); if (rawResult.execution_stats) { log.debug(`Mango execution stats: ${JSON.stringify(rawResult.execution_stats)}`); } if (!this.clazz) throw new core_1.PagingError("No statement target defined"); const id = decorator_validation_1.Model.pk(this.clazz); const type = decoration_1.Metadata.get(this.clazz, decoration_1.Metadata.key(db_decorators_1.DBKeys.ID, id))?.type; const pageResults = statement.fields && statement.fields.length ? docs // has fields means its not full model : docs.map((d) => { return this.adapter.revert(d, this.clazz, core_1.Sequence.parseValue(type, d[id]), undefined, ctx); }); this._bookmark = this.isValidBookmark(nextBookmark) ? nextBookmark : undefined; this._currentPage = page; if (this.isValidBookmark(nextBookmark)) { this._bookmarks.set(page + 1, nextBookmark); } if (!this.isValidBookmark(nextBookmark) || docs.length < this.size) { this._knownLastPage = page; this._totalPages = page; this._recordCount = (page - 1) * this.size + docs.length; } return pageResults; } } exports.CouchDBPaginator = CouchDBPaginator; //# sourceMappingURL=Paginator.js.map //# sourceMappingURL=Paginator.cjs.map