UNPKG

@naturalcycles/db-lib

Version:

Lowest Common Denominator API to supported Databases

220 lines (219 loc) 12.1 kB
import type { Transform } from 'node:stream'; import type { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib/json-schema'; import type { CommonLogger } from '@naturalcycles/js-lib/log'; import type { AsyncIndexedMapper, BaseDBEntity, StringMap, UnixTimestampMillis, Unsaved } from '@naturalcycles/js-lib/types'; import type { ReadableTyped } from '@naturalcycles/nodejs-lib/stream'; import type { CommonDBTransactionOptions, DBTransaction, RunQueryResult } from '../db.model.js'; import type { DBQuery } from '../query/dbQuery.js'; import { RunnableDBQuery } from '../query/dbQuery.js'; import type { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoPatchByIdOptions, CommonDaoPatchOptions, CommonDaoReadOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions } from './common.dao.model.js'; /** * Lowest common denominator API between supported Databases. * * DBM = Database model (how it's stored in DB) * BM = Backend model (optimized for API access) * TM = Transport model (optimized to be sent over the wire) */ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> { cfg: CommonDaoCfg<BM, DBM, ID>; constructor(cfg: CommonDaoCfg<BM, DBM, ID>); create(part?: Partial<BM>, opt?: CommonDaoOptions): BM; getById(id?: ID | null, opt?: CommonDaoReadOptions): Promise<BM | null>; getByIdOrEmpty(id: ID, part?: Partial<BM>, opt?: CommonDaoReadOptions): Promise<BM>; getByIdAsDBM(id?: ID | null, opt?: CommonDaoReadOptions): Promise<DBM | null>; getByIds(ids: ID[], opt?: CommonDaoReadOptions): Promise<BM[]>; getByIdsAsDBM(ids: ID[], opt?: CommonDaoReadOptions): Promise<DBM[]>; requireById(id: ID, opt?: CommonDaoReadOptions): Promise<BM>; requireByIdAsDBM(id: ID, opt?: CommonDaoReadOptions): Promise<DBM>; private throwRequiredError; /** * Throws if readOnly is true */ private requireWriteAccess; /** * Throws if readOnly is true */ private requireObjectMutability; private ensureUniqueId; getBy(by: keyof DBM, value: any, limit?: number, opt?: CommonDaoReadOptions): Promise<BM[]>; getOneBy(by: keyof DBM, value: any, opt?: CommonDaoReadOptions): Promise<BM | null>; getAll(opt?: CommonDaoReadOptions): Promise<BM[]>; /** * Pass `table` to override table */ query(table?: string): RunnableDBQuery<BM, DBM, ID>; runQuery(q: DBQuery<DBM>, opt?: CommonDaoReadOptions): Promise<BM[]>; runQuerySingleColumn<T = any>(q: DBQuery<DBM>, opt?: CommonDaoReadOptions): Promise<T[]>; /** * Convenience method that runs multiple queries in parallel and then merges their results together. * Does deduplication by id. * Order is not guaranteed, as queries run in parallel. */ runUnionQueries(queries: DBQuery<DBM>[], opt?: CommonDaoReadOptions): Promise<BM[]>; runQueryExtended(q: DBQuery<DBM>, opt?: CommonDaoReadOptions): Promise<RunQueryResult<BM>>; runQueryAsDBM(q: DBQuery<DBM>, opt?: CommonDaoReadOptions): Promise<DBM[]>; runQueryExtendedAsDBM(q: DBQuery<DBM>, opt?: CommonDaoReadOptions): Promise<RunQueryResult<DBM>>; runQueryCount(q: DBQuery<DBM>, opt?: CommonDaoReadOptions): Promise<number>; streamQueryForEach(q: DBQuery<DBM>, mapper: AsyncIndexedMapper<BM, void>, opt?: CommonDaoStreamForEachOptions<BM>): Promise<void>; streamQueryAsDBMForEach(q: DBQuery<DBM>, mapper: AsyncIndexedMapper<DBM, void>, opt?: CommonDaoStreamForEachOptions<DBM>): Promise<void>; /** * Stream as Readable, to be able to .pipe() it further with support of backpressure. */ streamQueryAsDBM(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<DBM>; /** * Stream as Readable, to be able to .pipe() it further with support of backpressure. * * Please note that this stream is currently not async-iteration friendly, because of * `through2` usage. * Will be migrated/fixed at some point in the future. * * You can do `.pipe(transformNoOp)` to make it "valid again". */ streamQuery(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<BM>): ReadableTyped<BM>; queryIds(q: DBQuery<DBM>, opt?: CommonDaoReadOptions): Promise<ID[]>; streamQueryIds(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<ID>): ReadableTyped<ID>; streamQueryIdsForEach(q: DBQuery<DBM>, mapper: AsyncIndexedMapper<ID, void>, opt?: CommonDaoStreamForEachOptions<ID>): Promise<void>; /** * Mutates! * "Returns", just to have a type of "Saved" */ assignIdCreatedUpdated<T extends BaseDBEntity>(obj: Partial<T>, opt?: CommonDaoOptions): T; /** * Convenience method to replace 3 operations (loading+patching+saving) with one: * * 1. Loads the row by id. * 1.1 Creates the row (via this.create()) if it doesn't exist * (this will cause a validation error if Patch has not enough data for the row to be valid). * 2. Applies the patch on top of loaded data. * 3. Saves (as fast as possible since the read) with the Patch applied, but only if the data has changed. */ patchById(id: ID, patch: Partial<BM>, opt?: CommonDaoPatchByIdOptions<DBM>): Promise<BM>; /** * Like patchById, but runs all operations within a Transaction. */ patchByIdInTransaction(id: ID, patch: Partial<BM>, opt?: CommonDaoPatchByIdOptions<DBM>): Promise<BM>; /** * Same as patchById, but takes the whole object as input. * This "whole object" is mutated with the patch and returned. * Otherwise, similar behavior as patchById. * It still loads the row from the DB. */ patch(bm: BM, patch: Partial<BM>, opt?: CommonDaoPatchOptions<DBM>): Promise<BM>; /** * Like patch, but runs all operations within a Transaction. */ patchInTransaction(bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM>; /** * Mutates with id, created, updated */ save(bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>; saveAsDBM(dbm: Unsaved<DBM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<DBM>; saveBatch(bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM[]>; saveBatchAsDBM(dbms: Unsaved<DBM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<DBM[]>; /** * "Streaming" is implemented by buffering incoming rows into **batches** * (of size opt.chunkSize, which defaults to 500), * and then executing db.saveBatch(chunk) with the concurrency * of opt.chunkConcurrency (which defaults to 32). */ streamSaveTransform(opt?: CommonDaoStreamSaveOptions<DBM>): Transform[]; /** * @returns number of deleted items */ deleteById(id?: ID | null, opt?: CommonDaoOptions): Promise<number>; deleteByIds(ids: ID[], opt?: CommonDaoOptions): Promise<number>; /** * Pass `chunkSize: number` (e.g 500) option to use Streaming: it will Stream the query, chunk by 500, and execute * `deleteByIds` for each chunk concurrently (infinite concurrency). * This is expected to be more memory-efficient way of deleting large number of rows. */ deleteByQuery(q: DBQuery<DBM>, opt?: CommonDaoStreamDeleteOptions<DBM>): Promise<number>; patchByIds(ids: ID[], patch: Partial<DBM>, opt?: CommonDaoOptions): Promise<number>; patchByQuery(q: DBQuery<DBM>, patch: Partial<DBM>, opt?: CommonDaoOptions): Promise<number>; /** * Caveat: it doesn't update created/updated props. * * @experimental */ increment(prop: keyof DBM, id: ID, by?: number, opt?: CommonDaoOptions): Promise<number>; /** * Caveat: it doesn't update created/updated props. * * @experimental */ incrementBatch(prop: keyof DBM, incrementMap: StringMap<number>, opt?: CommonDaoOptions): Promise<StringMap<number>>; dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): Promise<undefined>; dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): Promise<BM>; dbmsToBM(dbms: DBM[], opt?: CommonDaoOptions): Promise<BM[]>; /** * Mutates object with properties: id, created, updated. * Returns DBM (new reference). */ bmToDBM(bm: undefined, opt?: CommonDaoOptions): Promise<undefined>; bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<DBM>; bmsToDBM(bms: BM[], opt?: CommonDaoOptions): Promise<DBM[]>; anyToDBM(dbm: undefined, opt?: CommonDaoOptions): undefined; anyToDBM(dbm?: any, opt?: CommonDaoOptions): DBM; anyToDBMs(entities: DBM[], opt?: CommonDaoOptions): DBM[]; /** * Returns *converted value* (NOT the same reference). * Does NOT mutate the object. * Validates (unless `skipValidation=true` passed). */ private validateAndConvert; getTableSchema(): Promise<JsonSchemaRootObject<DBM>>; createTable(schema: JsonSchemaObject<DBM>, opt?: CommonDaoCreateOptions): Promise<void>; /** * Proxy to this.cfg.db.ping */ ping(): Promise<void>; createTransaction(opt?: CommonDBTransactionOptions): Promise<CommonDaoTransaction>; runInTransaction<T = void>(fn: CommonDaoTransactionFn<T>, opt?: CommonDBTransactionOptions): Promise<T>; /** * Throws if query uses a property that is in `excludeFromIndexes` list. */ private validateQueryIndexes; protected logResult(started: UnixTimestampMillis, op: string, res: any, table: string): void; protected logSaveResult(started: UnixTimestampMillis, op: string, table: string): void; protected logStarted(op: string, table: string, force?: boolean): UnixTimestampMillis; protected logSaveStarted(op: string, items: any, table: string): UnixTimestampMillis; } /** * Transaction is committed when the function returns resolved Promise (aka "returns normally"). * * Transaction is rolled back when the function returns rejected Promise (aka "throws"). */ export type CommonDaoTransactionFn<T = void> = (tx: CommonDaoTransaction) => Promise<T>; /** * Transaction context. * Has similar API than CommonDao, but all operations are performed in the context of the transaction. */ export declare class CommonDaoTransaction { tx: DBTransaction; private logger; constructor(tx: DBTransaction, logger: CommonLogger); /** * Commits the underlying DBTransaction. * May throw. */ commit(): Promise<void>; /** * Perform a graceful rollback without throwing/re-throwing any error. * Never throws. */ rollback(): Promise<void>; getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoReadOptions): Promise<BM | null>; getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoReadOptions): Promise<BM[]>; save<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>; saveBatch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM[]>; /** * DaoTransaction.patch does not load from DB. * It assumes the bm was previously loaded in the same Transaction, hence could not be * concurrently modified. Hence it's safe to not sync with DB. * * So, this method is a rather simple convenience "Object.assign and then save". */ patch<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>; deleteById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoOptions): Promise<number>; deleteByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoOptions): Promise<number>; }