import { Transform } from 'node:stream';
import { AsyncMapper, BaseDBEntity, CommonLogger, JsonSchemaObject, JsonSchemaRootObject, StringMap, UnixTimestampMillis, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
import { AjvSchema, ObjectSchema, ReadableTyped } from '@naturalcycles/nodejs-lib';
import { CommonDBTransactionOptions, DBTransaction, RunQueryResult } from '../db.model';
import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoPatchByIdOptions, CommonDaoPatchOptions, CommonDaoReadOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions } from './common.dao.model';
/**
 * 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: AsyncMapper<BM, void>, opt?: CommonDaoStreamForEachOptions<BM>): Promise<void>;
    streamQueryAsDBMForEach(q: DBQuery<DBM>, mapper: AsyncMapper<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: AsyncMapper<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).
     */
    validateAndConvert<T>(obj: Partial<T>, schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined, op?: 'load' | 'save', // this is to skip validation if validateOnLoad/Save is false
    opt?: CommonDaoOptions): any;
    getTableSchema(): Promise<JsonSchemaRootObject<DBM>>;
    createTable(schema: JsonSchemaObject<DBM>, opt?: CommonDaoCreateOptions): Promise<void>;
    /**
     * Proxy to this.cfg.db.ping
     */
    ping(): Promise<void>;
    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);
    /**
     * Perform a graceful rollback without throwing/re-throwing any error.
     */
    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>;
}
