@decaf-ts/for-couchdb
Version:
decaf-ts couchdb wrappers
174 lines • 7.35 kB
JavaScript
"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