@naturalcycles/db-lib
Version:
Lowest Common Denominator API to supported Databases
220 lines (219 loc) • 12.1 kB
TypeScript
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>;
}