@naturalcycles/db-lib 10.18.0 → 10.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commondao/common.dao.d.ts +39 -19
- package/dist/commondao/common.dao.js +73 -38
- package/dist/commondao/common.dao.model.d.ts +1 -14
- package/dist/commondb/base.common.db.d.ts +3 -3
- package/dist/commondb/base.common.db.js +3 -3
- package/dist/commondb/common.db.d.ts +7 -11
- package/dist/inmemory/inMemory.db.d.ts +3 -3
- package/dist/inmemory/inMemory.db.js +3 -3
- package/dist/query/dbQuery.d.ts +1 -1
- package/package.json +1 -1
- package/src/commondao/common.dao.model.ts +1 -22
- package/src/commondao/common.dao.ts +139 -68
- package/src/commondb/base.common.db.ts +3 -3
- package/src/commondb/common.db.ts +8 -13
- package/src/inmemory/inMemory.db.ts +3 -3
- package/src/query/dbQuery.ts +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Transform } from 'node:stream';
|
|
2
2
|
import type { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib/json-schema';
|
|
3
3
|
import type { CommonLogger } from '@naturalcycles/js-lib/log';
|
|
4
|
-
import type
|
|
4
|
+
import { type AsyncIndexedMapper, type BaseDBEntity, type NonNegativeInteger, type StringMap, type UnixTimestampMillis, type Unsaved } from '@naturalcycles/js-lib/types';
|
|
5
5
|
import { type ReadableTyped } from '@naturalcycles/nodejs-lib/stream';
|
|
6
6
|
import type { CommonDBTransactionOptions, DBTransaction, RunQueryResult } from '../db.model.js';
|
|
7
7
|
import type { DBQuery } from '../query/dbQuery.js';
|
|
8
8
|
import { RunnableDBQuery } from '../query/dbQuery.js';
|
|
9
|
-
import type { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoPatchByIdOptions, CommonDaoPatchOptions, CommonDaoReadOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions
|
|
9
|
+
import type { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoPatchByIdOptions, CommonDaoPatchOptions, CommonDaoReadOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions } from './common.dao.model.js';
|
|
10
10
|
/**
|
|
11
11
|
* Lowest common denominator API between supported Databases.
|
|
12
12
|
*
|
|
@@ -14,7 +14,7 @@ import type { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoP
|
|
|
14
14
|
* BM = Backend model (optimized for API access)
|
|
15
15
|
* TM = Transport model (optimized to be sent over the wire)
|
|
16
16
|
*/
|
|
17
|
-
export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> {
|
|
17
|
+
export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID extends string = BM['id']> {
|
|
18
18
|
cfg: CommonDaoCfg<BM, DBM, ID>;
|
|
19
19
|
constructor(cfg: CommonDaoCfg<BM, DBM, ID>);
|
|
20
20
|
create(part?: Partial<BM>, opt?: CommonDaoOptions): BM;
|
|
@@ -167,22 +167,26 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
167
167
|
* Proxy to this.cfg.db.ping
|
|
168
168
|
*/
|
|
169
169
|
ping(): Promise<void>;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
withId(id: ID): DaoWithId<typeof this>;
|
|
171
|
+
withIds(ids: ID[]): DaoWithIds<typeof this>;
|
|
172
|
+
toSave(input: BM | BM[]): DaoWithRows<typeof this>;
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
* Very @experimental.
|
|
174
|
+
* Load rows (by their ids) from Multiple tables at once.
|
|
175
|
+
* An optimized way to load data, minimizing DB round-trips.
|
|
176
|
+
*
|
|
177
|
+
* @experimental.
|
|
179
178
|
*/
|
|
180
|
-
static
|
|
179
|
+
static multiGet<MAP extends Record<string, DaoWithIds<AnyDao> | DaoWithId<AnyDao>>>(inputMap: MAP, opt?: CommonDaoReadOptions): Promise<{
|
|
180
|
+
[K in keyof MAP]: MAP[K] extends DaoWithIds<any> ? InferBM<MAP[K]['dao']>[] : InferBM<MAP[K]['dao']> | null;
|
|
181
|
+
}>;
|
|
182
|
+
private static prepareMultiGetIds;
|
|
183
|
+
private static multiGetMapByTableById;
|
|
184
|
+
private static prepareMultiGetOutput;
|
|
181
185
|
/**
|
|
182
186
|
* Very @experimental.
|
|
183
187
|
*/
|
|
184
|
-
static multiDeleteByIds(inputs: DaoWithIds[], _opt?: CommonDaoOptions): Promise<NonNegativeInteger>;
|
|
185
|
-
static
|
|
188
|
+
static multiDeleteByIds(inputs: DaoWithIds<any>[], _opt?: CommonDaoOptions): Promise<NonNegativeInteger>;
|
|
189
|
+
static multiSave(inputs: DaoWithRows<any>[], opt?: CommonDaoSaveBatchOptions<any>): Promise<void>;
|
|
186
190
|
createTransaction(opt?: CommonDBTransactionOptions): Promise<CommonDaoTransaction>;
|
|
187
191
|
runInTransaction<T = void>(fn: CommonDaoTransactionFn<T>, opt?: CommonDBTransactionOptions): Promise<T>;
|
|
188
192
|
/**
|
|
@@ -218,8 +222,8 @@ export declare class CommonDaoTransaction {
|
|
|
218
222
|
* Never throws.
|
|
219
223
|
*/
|
|
220
224
|
rollback(): Promise<void>;
|
|
221
|
-
getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoReadOptions): Promise<BM | null>;
|
|
222
|
-
getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoReadOptions): Promise<BM[]>;
|
|
225
|
+
getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID extends string = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoReadOptions): Promise<BM | null>;
|
|
226
|
+
getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID extends string = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoReadOptions): Promise<BM[]>;
|
|
223
227
|
save<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
|
|
224
228
|
saveBatch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM[]>;
|
|
225
229
|
/**
|
|
@@ -229,7 +233,23 @@ export declare class CommonDaoTransaction {
|
|
|
229
233
|
*
|
|
230
234
|
* So, this method is a rather simple convenience "Object.assign and then save".
|
|
231
235
|
*/
|
|
232
|
-
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>;
|
|
233
|
-
deleteById<
|
|
234
|
-
deleteByIds<
|
|
236
|
+
patch<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID extends string = BM['id']>(dao: CommonDao<BM, DBM, ID>, bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
|
|
237
|
+
deleteById<DAO extends AnyDao>(dao: DAO, id?: InferID<DAO> | null, opt?: CommonDaoOptions): Promise<number>;
|
|
238
|
+
deleteByIds<DAO extends AnyDao>(dao: DAO, ids: InferID<DAO>[], opt?: CommonDaoOptions): Promise<number>;
|
|
239
|
+
}
|
|
240
|
+
export interface DaoWithIds<DAO extends AnyDao> {
|
|
241
|
+
dao: DAO;
|
|
242
|
+
ids: string[];
|
|
243
|
+
}
|
|
244
|
+
export interface DaoWithId<DAO extends AnyDao> {
|
|
245
|
+
dao: DAO;
|
|
246
|
+
id: string;
|
|
247
|
+
}
|
|
248
|
+
export interface DaoWithRows<DAO extends AnyDao> {
|
|
249
|
+
dao: DAO;
|
|
250
|
+
rows: InferBM<DAO>[];
|
|
235
251
|
}
|
|
252
|
+
type InferBM<DAO> = DAO extends CommonDao<infer BM> ? BM : never;
|
|
253
|
+
type InferID<DAO> = DAO extends CommonDao<any, any, infer ID> ? ID : never;
|
|
254
|
+
export type AnyDao = CommonDao<any>;
|
|
255
|
+
export {};
|
|
@@ -7,6 +7,7 @@ import { _deepJsonEquals } from '@naturalcycles/js-lib/object/deepEquals.js';
|
|
|
7
7
|
import { _deepCopy, _filterUndefinedValues, _objectAssignExact, } from '@naturalcycles/js-lib/object/object.util.js';
|
|
8
8
|
import { pMap } from '@naturalcycles/js-lib/promise/pMap.js';
|
|
9
9
|
import { _truncate } from '@naturalcycles/js-lib/string/string.util.js';
|
|
10
|
+
import { _stringMapEntries, _stringMapValues, } from '@naturalcycles/js-lib/types';
|
|
10
11
|
import { _passthroughPredicate, _typeCast, SKIP } from '@naturalcycles/js-lib/types';
|
|
11
12
|
import { stringId } from '@naturalcycles/nodejs-lib';
|
|
12
13
|
import { transformFlatten } from '@naturalcycles/nodejs-lib/stream';
|
|
@@ -981,63 +982,96 @@ export class CommonDao {
|
|
|
981
982
|
async ping() {
|
|
982
983
|
await this.cfg.db.ping();
|
|
983
984
|
}
|
|
984
|
-
|
|
985
|
+
withId(id) {
|
|
985
986
|
return {
|
|
986
987
|
dao: this,
|
|
987
988
|
id,
|
|
988
989
|
};
|
|
989
990
|
}
|
|
990
|
-
|
|
991
|
+
withIds(ids) {
|
|
991
992
|
return {
|
|
992
993
|
dao: this,
|
|
993
994
|
ids,
|
|
994
995
|
};
|
|
995
996
|
}
|
|
996
|
-
|
|
997
|
+
toSave(input) {
|
|
997
998
|
return {
|
|
998
999
|
dao: this,
|
|
999
|
-
rows,
|
|
1000
|
+
rows: [input].flat(),
|
|
1000
1001
|
};
|
|
1001
1002
|
}
|
|
1002
1003
|
/**
|
|
1003
|
-
*
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
dao: input.dao,
|
|
1008
|
-
ids: [input.id],
|
|
1009
|
-
}));
|
|
1010
|
-
const rowsByTable = await CommonDao.multiGetByIds(inputs2, opt);
|
|
1011
|
-
const results = inputs.map(({ dao }) => {
|
|
1012
|
-
const { table } = dao.cfg;
|
|
1013
|
-
return rowsByTable[table]?.[0] || null;
|
|
1014
|
-
});
|
|
1015
|
-
return results;
|
|
1016
|
-
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Very @experimental.
|
|
1004
|
+
* Load rows (by their ids) from Multiple tables at once.
|
|
1005
|
+
* An optimized way to load data, minimizing DB round-trips.
|
|
1006
|
+
*
|
|
1007
|
+
* @experimental.
|
|
1019
1008
|
*/
|
|
1020
|
-
static async
|
|
1021
|
-
|
|
1009
|
+
static async multiGet(inputMap, opt = {}) {
|
|
1010
|
+
const db = Object.values(inputMap)[0]?.dao.cfg.db;
|
|
1011
|
+
if (!db) {
|
|
1022
1012
|
return {};
|
|
1023
|
-
const { db } = inputs[0].dao.cfg;
|
|
1024
|
-
const idsByTable = {};
|
|
1025
|
-
for (const { dao, ids } of inputs) {
|
|
1026
|
-
const { table } = dao.cfg;
|
|
1027
|
-
idsByTable[table] = ids;
|
|
1028
1013
|
}
|
|
1014
|
+
const idsByTable = CommonDao.prepareMultiGetIds(inputMap);
|
|
1029
1015
|
// todo: support tx
|
|
1030
|
-
const dbmsByTable = await db.
|
|
1031
|
-
const
|
|
1032
|
-
await
|
|
1016
|
+
const dbmsByTable = await db.multiGet(idsByTable, opt);
|
|
1017
|
+
const dbmByTableById = CommonDao.multiGetMapByTableById(dbmsByTable);
|
|
1018
|
+
return (await CommonDao.prepareMultiGetOutput(inputMap, dbmByTableById, opt));
|
|
1019
|
+
}
|
|
1020
|
+
static prepareMultiGetIds(inputMap) {
|
|
1021
|
+
const idSetByTable = {};
|
|
1022
|
+
for (const input of _stringMapValues(inputMap)) {
|
|
1023
|
+
const { table } = input.dao.cfg;
|
|
1024
|
+
idSetByTable[table] ||= new Set();
|
|
1025
|
+
if ('id' in input) {
|
|
1026
|
+
// Singular
|
|
1027
|
+
idSetByTable[table].add(input.id);
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
// Plural
|
|
1031
|
+
for (const id of input.ids) {
|
|
1032
|
+
idSetByTable[table].add(id);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
const idsByTable = {};
|
|
1037
|
+
for (const [table, idSet] of _stringMapEntries(idSetByTable)) {
|
|
1038
|
+
idsByTable[table] = [...idSet];
|
|
1039
|
+
}
|
|
1040
|
+
return idsByTable;
|
|
1041
|
+
}
|
|
1042
|
+
static multiGetMapByTableById(dbmsByTable) {
|
|
1043
|
+
// We create this "map of maps", to be able to track the results back to the input props
|
|
1044
|
+
// This is needed to support:
|
|
1045
|
+
// - having multiple props from the same table
|
|
1046
|
+
const dbmByTableById = {};
|
|
1047
|
+
for (const [table, dbms] of _stringMapEntries(dbmsByTable)) {
|
|
1048
|
+
dbmByTableById[table] ||= {};
|
|
1049
|
+
for (const dbm of dbms) {
|
|
1050
|
+
dbmByTableById[table][dbm.id] = dbm;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return dbmByTableById;
|
|
1054
|
+
}
|
|
1055
|
+
static async prepareMultiGetOutput(inputMap, dbmByTableById, opt = {}) {
|
|
1056
|
+
const bmsByProp = {};
|
|
1057
|
+
// Loop over input props again, to produce the output of the same shape as requested
|
|
1058
|
+
await pMap(_stringMapEntries(inputMap), async ([prop, input]) => {
|
|
1059
|
+
const { dao } = input;
|
|
1033
1060
|
const { table } = dao.cfg;
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1061
|
+
if ('id' in input) {
|
|
1062
|
+
// Singular
|
|
1063
|
+
const dbm = dbmByTableById[table][input.id];
|
|
1064
|
+
bmsByProp[prop] = (await dao.dbmToBM(dbm, opt)) || null;
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
// Plural
|
|
1068
|
+
// We apply filtering, to be able to support multiple input props fetching from the same table.
|
|
1069
|
+
// Without filtering - every prop will get ALL rows from that table.
|
|
1070
|
+
const dbms = input.ids.map(id => dbmByTableById[table][id]).filter(_isTruthy);
|
|
1071
|
+
bmsByProp[prop] = await dao.dbmsToBM(dbms, opt);
|
|
1037
1072
|
}
|
|
1038
|
-
bmsByTable[table] = await dao.dbmsToBM(dbms, opt);
|
|
1039
1073
|
});
|
|
1040
|
-
return
|
|
1074
|
+
return bmsByProp;
|
|
1041
1075
|
}
|
|
1042
1076
|
/**
|
|
1043
1077
|
* Very @experimental.
|
|
@@ -1050,9 +1084,9 @@ export class CommonDao {
|
|
|
1050
1084
|
for (const { dao, ids } of inputs) {
|
|
1051
1085
|
idsByTable[dao.cfg.table] = ids;
|
|
1052
1086
|
}
|
|
1053
|
-
return await db.
|
|
1087
|
+
return await db.multiDelete(idsByTable);
|
|
1054
1088
|
}
|
|
1055
|
-
static async
|
|
1089
|
+
static async multiSave(inputs, opt = {}) {
|
|
1056
1090
|
if (!inputs.length)
|
|
1057
1091
|
return;
|
|
1058
1092
|
const { db } = inputs[0].dao.cfg;
|
|
@@ -1062,7 +1096,7 @@ export class CommonDao {
|
|
|
1062
1096
|
rows.forEach(bm => dao.assignIdCreatedUpdated(bm, opt));
|
|
1063
1097
|
dbmsByTable[table] = await dao.bmsToDBM(rows, opt);
|
|
1064
1098
|
});
|
|
1065
|
-
await db.
|
|
1099
|
+
await db.multiSave(dbmsByTable);
|
|
1066
1100
|
}
|
|
1067
1101
|
async createTransaction(opt) {
|
|
1068
1102
|
const tx = await this.cfg.db.createTransaction(opt);
|
|
@@ -1217,6 +1251,7 @@ export class CommonDaoTransaction {
|
|
|
1217
1251
|
Object.assign(bm, patch);
|
|
1218
1252
|
return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx });
|
|
1219
1253
|
}
|
|
1254
|
+
// todo: use AnyDao/Infer in other methods as well, if this works well
|
|
1220
1255
|
async deleteById(dao, id, opt) {
|
|
1221
1256
|
if (!id)
|
|
1222
1257
|
return 0;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { ValidationFunction } from '@naturalcycles/js-lib';
|
|
2
2
|
import type { AppError, ErrorMode } from '@naturalcycles/js-lib/error';
|
|
3
3
|
import type { CommonLogger } from '@naturalcycles/js-lib/log';
|
|
4
|
-
import type { BaseDBEntity,
|
|
4
|
+
import type { BaseDBEntity, Promisable, UnixTimestamp } from '@naturalcycles/js-lib/types';
|
|
5
5
|
import type { TransformLogProgressOptions, TransformMapOptions } from '@naturalcycles/nodejs-lib/stream';
|
|
6
6
|
import type { CommonDB } from '../commondb/common.db.js';
|
|
7
7
|
import type { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model.js';
|
|
8
|
-
import type { CommonDao } from './common.dao.js';
|
|
9
8
|
export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']> {
|
|
10
9
|
/**
|
|
11
10
|
* Allows to override the id generation function.
|
|
@@ -333,15 +332,3 @@ export interface CommonDaoStreamOptions<IN> extends CommonDaoReadOptions, Transf
|
|
|
333
332
|
chunkConcurrency?: number;
|
|
334
333
|
}
|
|
335
334
|
export type CommonDaoCreateOptions = CommonDBCreateOptions;
|
|
336
|
-
export interface DaoWithIds {
|
|
337
|
-
dao: CommonDao<any>;
|
|
338
|
-
ids: string[];
|
|
339
|
-
}
|
|
340
|
-
export interface DaoWithId {
|
|
341
|
-
dao: CommonDao<any>;
|
|
342
|
-
id: string;
|
|
343
|
-
}
|
|
344
|
-
export interface DaoWithRows<ROW extends ObjectWithId = ObjectWithId> {
|
|
345
|
-
dao: CommonDao<any>;
|
|
346
|
-
rows: ROW[];
|
|
347
|
-
}
|
|
@@ -28,7 +28,7 @@ export declare class BaseCommonDB implements CommonDB {
|
|
|
28
28
|
runInTransaction(fn: DBTransactionFn, _opt?: CommonDBTransactionOptions): Promise<void>;
|
|
29
29
|
createTransaction(_opt?: CommonDBTransactionOptions): Promise<DBTransaction>;
|
|
30
30
|
incrementBatch(_table: string, _prop: string, _incrementMap: StringMap<number>, _opt?: CommonDBOptions): Promise<StringMap<number>>;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
multiGet<ROW extends ObjectWithId>(_map: StringMap<string[]>, _opt?: CommonDBReadOptions): Promise<StringMap<ROW[]>>;
|
|
32
|
+
multiSave<ROW extends ObjectWithId>(_map: StringMap<ROW[]>, _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
33
|
+
multiDelete(_map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<number>;
|
|
34
34
|
}
|
|
@@ -57,13 +57,13 @@ export class BaseCommonDB {
|
|
|
57
57
|
async incrementBatch(_table, _prop, _incrementMap, _opt) {
|
|
58
58
|
throw new Error('incrementBatch is not implemented');
|
|
59
59
|
}
|
|
60
|
-
async
|
|
60
|
+
async multiGet(_map, _opt) {
|
|
61
61
|
throw new Error('multiGetByIds is not implemented');
|
|
62
62
|
}
|
|
63
|
-
async
|
|
63
|
+
async multiSave(_map, _opt) {
|
|
64
64
|
throw new Error('multiSaveBatch is not implemented');
|
|
65
65
|
}
|
|
66
|
-
async
|
|
66
|
+
async multiDelete(_map, _opt) {
|
|
67
67
|
throw new Error('multiDeleteByIds is not implemented');
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -3,14 +3,6 @@ import type { NonNegativeInteger, ObjectWithId, StringMap } from '@naturalcycles
|
|
|
3
3
|
import type { ReadableTyped } from '@naturalcycles/nodejs-lib/stream';
|
|
4
4
|
import type { CommonDBCreateOptions, CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions, CommonDBStreamOptions, CommonDBTransactionOptions, DBTransaction, DBTransactionFn, RunQueryResult } from '../db.model.js';
|
|
5
5
|
import type { DBQuery } from '../query/dbQuery.js';
|
|
6
|
-
export declare enum CommonDBType {
|
|
7
|
-
'document' = "document",
|
|
8
|
-
'relational' = "relational"
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* A tuple that contains the table name and the id.
|
|
12
|
-
*/
|
|
13
|
-
export type CommonDBKey = [table: string, id: string];
|
|
14
6
|
export interface CommonDB {
|
|
15
7
|
/**
|
|
16
8
|
* Relational databases are expected to return `null` for all missing properties.
|
|
@@ -64,7 +56,7 @@ export interface CommonDB {
|
|
|
64
56
|
*
|
|
65
57
|
* @experimental
|
|
66
58
|
*/
|
|
67
|
-
|
|
59
|
+
multiGet: <ROW extends ObjectWithId>(idsByTable: StringMap<string[]>, opt?: CommonDBReadOptions) => Promise<StringMap<ROW[]>>;
|
|
68
60
|
/**
|
|
69
61
|
* Order by 'id' is not supported by all implementations (for example, Datastore doesn't support it).
|
|
70
62
|
*/
|
|
@@ -88,7 +80,7 @@ export interface CommonDB {
|
|
|
88
80
|
*
|
|
89
81
|
* @experimental
|
|
90
82
|
*/
|
|
91
|
-
|
|
83
|
+
multiSave: <ROW extends ObjectWithId>(rowsByTable: StringMap<ROW[]>, opt?: CommonDBSaveOptions<ROW>) => Promise<void>;
|
|
92
84
|
/**
|
|
93
85
|
* Perform a partial update of a row by its id.
|
|
94
86
|
* Unlike save - doesn't require to first load the doc.
|
|
@@ -119,7 +111,7 @@ export interface CommonDB {
|
|
|
119
111
|
*
|
|
120
112
|
* @experimental
|
|
121
113
|
*/
|
|
122
|
-
|
|
114
|
+
multiDelete: (idsByTable: StringMap<string[]>, opt?: CommonDBOptions) => Promise<NonNegativeInteger>;
|
|
123
115
|
/**
|
|
124
116
|
* Returns number of deleted items.
|
|
125
117
|
* Not supported by all implementations (e.g Datastore will always return same number as number of ids).
|
|
@@ -172,6 +164,10 @@ export interface CommonDB {
|
|
|
172
164
|
*/
|
|
173
165
|
incrementBatch: (table: string, prop: string, incrementMap: StringMap<number>, opt?: CommonDBOptions) => Promise<StringMap<number>>;
|
|
174
166
|
}
|
|
167
|
+
export declare enum CommonDBType {
|
|
168
|
+
'document' = "document",
|
|
169
|
+
'relational' = "relational"
|
|
170
|
+
}
|
|
175
171
|
/**
|
|
176
172
|
* Manifest of supported features.
|
|
177
173
|
*/
|
|
@@ -50,13 +50,13 @@ export declare class InMemoryDB implements CommonDB {
|
|
|
50
50
|
getTableSchema<ROW extends ObjectWithId>(_table: string): Promise<JsonSchemaRootObject<ROW>>;
|
|
51
51
|
createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>, opt?: CommonDBCreateOptions): Promise<void>;
|
|
52
52
|
getByIds<ROW extends ObjectWithId>(_table: string, ids: string[], _opt?: CommonDBOptions): Promise<ROW[]>;
|
|
53
|
-
|
|
53
|
+
multiGet<ROW extends ObjectWithId>(map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<StringMap<ROW[]>>;
|
|
54
54
|
saveBatch<ROW extends ObjectWithId>(_table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
55
|
-
|
|
55
|
+
multiSave<ROW extends ObjectWithId>(map: StringMap<ROW[]>, opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
56
56
|
patchById<ROW extends ObjectWithId>(_table: string, id: string, patch: Partial<ROW>, _opt?: CommonDBOptions): Promise<void>;
|
|
57
57
|
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
|
|
58
58
|
deleteByIds(_table: string, ids: string[], _opt?: CommonDBOptions): Promise<number>;
|
|
59
|
-
|
|
59
|
+
multiDelete(map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<number>;
|
|
60
60
|
patchByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: Partial<ROW>): Promise<number>;
|
|
61
61
|
runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<RunQueryResult<ROW>>;
|
|
62
62
|
runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
|
|
@@ -74,7 +74,7 @@ export class InMemoryDB {
|
|
|
74
74
|
this.data[table] ||= {};
|
|
75
75
|
return ids.map(id => this.data[table][id]).filter(Boolean);
|
|
76
76
|
}
|
|
77
|
-
async
|
|
77
|
+
async multiGet(map, _opt = {}) {
|
|
78
78
|
const result = {};
|
|
79
79
|
for (const [tableName, ids] of _stringMapEntries(map)) {
|
|
80
80
|
const table = this.cfg.tablesPrefix + tableName;
|
|
@@ -104,7 +104,7 @@ export class InMemoryDB {
|
|
|
104
104
|
this.data[table][r.id] = JSON.parse(JSON.stringify(r), bufferReviver);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
async
|
|
107
|
+
async multiSave(map, opt = {}) {
|
|
108
108
|
for (const [table, rows] of _stringMapEntries(map)) {
|
|
109
109
|
await this.saveBatch(table, rows, opt);
|
|
110
110
|
}
|
|
@@ -134,7 +134,7 @@ export class InMemoryDB {
|
|
|
134
134
|
}
|
|
135
135
|
return count;
|
|
136
136
|
}
|
|
137
|
-
async
|
|
137
|
+
async multiDelete(map, _opt) {
|
|
138
138
|
let count = 0;
|
|
139
139
|
for (const [table, ids] of _stringMapEntries(map)) {
|
|
140
140
|
count += await this.deleteByIds(table, ids, _opt);
|
package/dist/query/dbQuery.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ export declare class DBQuery<ROW extends ObjectWithId> {
|
|
|
87
87
|
/**
|
|
88
88
|
* DBQuery that has additional method to support Fluent API style.
|
|
89
89
|
*/
|
|
90
|
-
export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> extends DBQuery<DBM> {
|
|
90
|
+
export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID extends string = BM['id']> extends DBQuery<DBM> {
|
|
91
91
|
dao: CommonDao<BM, DBM, ID>;
|
|
92
92
|
/**
|
|
93
93
|
* Pass `table` to override table.
|
package/package.json
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import type { ValidationFunction } from '@naturalcycles/js-lib'
|
|
2
2
|
import type { AppError, ErrorMode } from '@naturalcycles/js-lib/error'
|
|
3
3
|
import type { CommonLogger } from '@naturalcycles/js-lib/log'
|
|
4
|
-
import type {
|
|
5
|
-
BaseDBEntity,
|
|
6
|
-
ObjectWithId,
|
|
7
|
-
Promisable,
|
|
8
|
-
UnixTimestamp,
|
|
9
|
-
} from '@naturalcycles/js-lib/types'
|
|
4
|
+
import type { BaseDBEntity, Promisable, UnixTimestamp } from '@naturalcycles/js-lib/types'
|
|
10
5
|
import type {
|
|
11
6
|
TransformLogProgressOptions,
|
|
12
7
|
TransformMapOptions,
|
|
13
8
|
} from '@naturalcycles/nodejs-lib/stream'
|
|
14
9
|
import type { CommonDB } from '../commondb/common.db.js'
|
|
15
10
|
import type { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model.js'
|
|
16
|
-
import type { CommonDao } from './common.dao.js'
|
|
17
11
|
|
|
18
12
|
export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']> {
|
|
19
13
|
/**
|
|
@@ -404,18 +398,3 @@ export interface CommonDaoStreamOptions<IN>
|
|
|
404
398
|
}
|
|
405
399
|
|
|
406
400
|
export type CommonDaoCreateOptions = CommonDBCreateOptions
|
|
407
|
-
|
|
408
|
-
export interface DaoWithIds {
|
|
409
|
-
dao: CommonDao<any>
|
|
410
|
-
ids: string[]
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
export interface DaoWithId {
|
|
414
|
-
dao: CommonDao<any>
|
|
415
|
-
id: string
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
export interface DaoWithRows<ROW extends ObjectWithId = ObjectWithId> {
|
|
419
|
-
dao: CommonDao<any>
|
|
420
|
-
rows: ROW[]
|
|
421
|
-
}
|
|
@@ -14,14 +14,16 @@ import {
|
|
|
14
14
|
} from '@naturalcycles/js-lib/object/object.util.js'
|
|
15
15
|
import { pMap } from '@naturalcycles/js-lib/promise/pMap.js'
|
|
16
16
|
import { _truncate } from '@naturalcycles/js-lib/string/string.util.js'
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
import {
|
|
18
|
+
_stringMapEntries,
|
|
19
|
+
_stringMapValues,
|
|
20
|
+
type AsyncIndexedMapper,
|
|
21
|
+
type BaseDBEntity,
|
|
22
|
+
type NonNegativeInteger,
|
|
23
|
+
type ObjectWithId,
|
|
24
|
+
type StringMap,
|
|
25
|
+
type UnixTimestampMillis,
|
|
26
|
+
type Unsaved,
|
|
25
27
|
} from '@naturalcycles/js-lib/types'
|
|
26
28
|
import { _passthroughPredicate, _typeCast, SKIP } from '@naturalcycles/js-lib/types'
|
|
27
29
|
import { stringId } from '@naturalcycles/nodejs-lib'
|
|
@@ -52,9 +54,6 @@ import type {
|
|
|
52
54
|
CommonDaoStreamForEachOptions,
|
|
53
55
|
CommonDaoStreamOptions,
|
|
54
56
|
CommonDaoStreamSaveOptions,
|
|
55
|
-
DaoWithId,
|
|
56
|
-
DaoWithIds,
|
|
57
|
-
DaoWithRows,
|
|
58
57
|
} from './common.dao.model.js'
|
|
59
58
|
import { CommonDaoLogLevel } from './common.dao.model.js'
|
|
60
59
|
|
|
@@ -65,7 +64,11 @@ import { CommonDaoLogLevel } from './common.dao.model.js'
|
|
|
65
64
|
* BM = Backend model (optimized for API access)
|
|
66
65
|
* TM = Transport model (optimized to be sent over the wire)
|
|
67
66
|
*/
|
|
68
|
-
export class CommonDao<
|
|
67
|
+
export class CommonDao<
|
|
68
|
+
BM extends BaseDBEntity,
|
|
69
|
+
DBM extends BaseDBEntity = BM,
|
|
70
|
+
ID extends string = BM['id'],
|
|
71
|
+
> {
|
|
69
72
|
constructor(public cfg: CommonDaoCfg<BM, DBM, ID>) {
|
|
70
73
|
this.cfg = {
|
|
71
74
|
logLevel: CommonDaoLogLevel.NONE,
|
|
@@ -1261,85 +1264,131 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, I
|
|
|
1261
1264
|
await this.cfg.db.ping()
|
|
1262
1265
|
}
|
|
1263
1266
|
|
|
1264
|
-
|
|
1267
|
+
withId(id: ID): DaoWithId<typeof this> {
|
|
1265
1268
|
return {
|
|
1266
1269
|
dao: this,
|
|
1267
1270
|
id,
|
|
1268
1271
|
}
|
|
1269
1272
|
}
|
|
1270
1273
|
|
|
1271
|
-
|
|
1274
|
+
withIds(ids: ID[]): DaoWithIds<typeof this> {
|
|
1272
1275
|
return {
|
|
1273
1276
|
dao: this,
|
|
1274
1277
|
ids,
|
|
1275
1278
|
}
|
|
1276
1279
|
}
|
|
1277
1280
|
|
|
1278
|
-
|
|
1281
|
+
toSave(input: BM | BM[]): DaoWithRows<typeof this> {
|
|
1279
1282
|
return {
|
|
1280
1283
|
dao: this,
|
|
1281
|
-
rows,
|
|
1284
|
+
rows: [input].flat() as any[],
|
|
1282
1285
|
}
|
|
1283
1286
|
}
|
|
1284
1287
|
|
|
1285
1288
|
/**
|
|
1286
|
-
*
|
|
1289
|
+
* Load rows (by their ids) from Multiple tables at once.
|
|
1290
|
+
* An optimized way to load data, minimizing DB round-trips.
|
|
1291
|
+
*
|
|
1292
|
+
* @experimental.
|
|
1287
1293
|
*/
|
|
1288
|
-
static async
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1294
|
+
static async multiGet<MAP extends Record<string, DaoWithIds<AnyDao> | DaoWithId<AnyDao>>>(
|
|
1295
|
+
inputMap: MAP,
|
|
1296
|
+
opt: CommonDaoReadOptions = {},
|
|
1297
|
+
): Promise<{
|
|
1298
|
+
[K in keyof MAP]: MAP[K] extends DaoWithIds<any>
|
|
1299
|
+
? InferBM<MAP[K]['dao']>[]
|
|
1300
|
+
: InferBM<MAP[K]['dao']> | null
|
|
1301
|
+
}> {
|
|
1302
|
+
const db = Object.values(inputMap)[0]?.dao.cfg.db
|
|
1303
|
+
if (!db) {
|
|
1304
|
+
return {} as any
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
const idsByTable = CommonDao.prepareMultiGetIds(inputMap)
|
|
1308
|
+
|
|
1309
|
+
// todo: support tx
|
|
1310
|
+
const dbmsByTable = await db.multiGet(idsByTable, opt)
|
|
1311
|
+
|
|
1312
|
+
const dbmByTableById = CommonDao.multiGetMapByTableById(dbmsByTable)
|
|
1299
1313
|
|
|
1300
|
-
return
|
|
1314
|
+
return (await CommonDao.prepareMultiGetOutput(inputMap, dbmByTableById, opt)) as any
|
|
1301
1315
|
}
|
|
1302
1316
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1317
|
+
private static prepareMultiGetIds(
|
|
1318
|
+
inputMap: StringMap<DaoWithIds<AnyDao> | DaoWithId<AnyDao>>,
|
|
1319
|
+
): StringMap<string[]> {
|
|
1320
|
+
const idSetByTable: StringMap<Set<string>> = {}
|
|
1321
|
+
|
|
1322
|
+
for (const input of _stringMapValues(inputMap)) {
|
|
1323
|
+
const { table } = input.dao.cfg
|
|
1324
|
+
idSetByTable[table] ||= new Set()
|
|
1325
|
+
if ('id' in input) {
|
|
1326
|
+
// Singular
|
|
1327
|
+
idSetByTable[table].add(input.id)
|
|
1328
|
+
} else {
|
|
1329
|
+
// Plural
|
|
1330
|
+
for (const id of input.ids) {
|
|
1331
|
+
idSetByTable[table].add(id)
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1312
1336
|
const idsByTable: StringMap<string[]> = {}
|
|
1313
|
-
for (const
|
|
1314
|
-
|
|
1315
|
-
|
|
1337
|
+
for (const [table, idSet] of _stringMapEntries(idSetByTable)) {
|
|
1338
|
+
idsByTable[table] = [...idSet]
|
|
1339
|
+
}
|
|
1340
|
+
return idsByTable
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
private static multiGetMapByTableById(
|
|
1344
|
+
dbmsByTable: StringMap<ObjectWithId[]>,
|
|
1345
|
+
): StringMap<StringMap<ObjectWithId>> {
|
|
1346
|
+
// We create this "map of maps", to be able to track the results back to the input props
|
|
1347
|
+
// This is needed to support:
|
|
1348
|
+
// - having multiple props from the same table
|
|
1349
|
+
const dbmByTableById: StringMap<StringMap<ObjectWithId>> = {}
|
|
1350
|
+
for (const [table, dbms] of _stringMapEntries(dbmsByTable)) {
|
|
1351
|
+
dbmByTableById[table] ||= {}
|
|
1352
|
+
for (const dbm of dbms) {
|
|
1353
|
+
dbmByTableById[table][dbm.id] = dbm
|
|
1354
|
+
}
|
|
1316
1355
|
}
|
|
1317
1356
|
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
const bmsByTable: StringMap<unknown[]> = {}
|
|
1357
|
+
return dbmByTableById
|
|
1358
|
+
}
|
|
1321
1359
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1360
|
+
private static async prepareMultiGetOutput(
|
|
1361
|
+
inputMap: StringMap<DaoWithIds<AnyDao> | DaoWithId<AnyDao>>,
|
|
1362
|
+
dbmByTableById: StringMap<StringMap<ObjectWithId>>,
|
|
1363
|
+
opt: CommonDaoReadOptions = {},
|
|
1364
|
+
): Promise<StringMap<unknown>> {
|
|
1365
|
+
const bmsByProp: StringMap<unknown> = {}
|
|
1325
1366
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1367
|
+
// Loop over input props again, to produce the output of the same shape as requested
|
|
1368
|
+
await pMap(_stringMapEntries(inputMap), async ([prop, input]) => {
|
|
1369
|
+
const { dao } = input
|
|
1370
|
+
const { table } = dao.cfg
|
|
1371
|
+
if ('id' in input) {
|
|
1372
|
+
// Singular
|
|
1373
|
+
const dbm = dbmByTableById[table]![input.id]
|
|
1374
|
+
bmsByProp[prop] = (await dao.dbmToBM(dbm, opt)) || null
|
|
1375
|
+
} else {
|
|
1376
|
+
// Plural
|
|
1377
|
+
// We apply filtering, to be able to support multiple input props fetching from the same table.
|
|
1378
|
+
// Without filtering - every prop will get ALL rows from that table.
|
|
1379
|
+
const dbms = input.ids.map(id => dbmByTableById[table]![id]).filter(_isTruthy)
|
|
1380
|
+
bmsByProp[prop] = await dao.dbmsToBM(dbms, opt)
|
|
1330
1381
|
}
|
|
1331
|
-
|
|
1332
|
-
bmsByTable[table] = await dao.dbmsToBM(dbms, opt)
|
|
1333
1382
|
})
|
|
1334
1383
|
|
|
1335
|
-
return
|
|
1384
|
+
return bmsByProp as any
|
|
1336
1385
|
}
|
|
1337
1386
|
|
|
1338
1387
|
/**
|
|
1339
1388
|
* Very @experimental.
|
|
1340
1389
|
*/
|
|
1341
1390
|
static async multiDeleteByIds(
|
|
1342
|
-
inputs: DaoWithIds[],
|
|
1391
|
+
inputs: DaoWithIds<any>[],
|
|
1343
1392
|
_opt: CommonDaoOptions = {},
|
|
1344
1393
|
): Promise<NonNegativeInteger> {
|
|
1345
1394
|
if (!inputs.length) return 0
|
|
@@ -1349,11 +1398,11 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, I
|
|
|
1349
1398
|
idsByTable[dao.cfg.table] = ids
|
|
1350
1399
|
}
|
|
1351
1400
|
|
|
1352
|
-
return await db.
|
|
1401
|
+
return await db.multiDelete(idsByTable)
|
|
1353
1402
|
}
|
|
1354
1403
|
|
|
1355
|
-
static async
|
|
1356
|
-
inputs: DaoWithRows[],
|
|
1404
|
+
static async multiSave(
|
|
1405
|
+
inputs: DaoWithRows<any>[],
|
|
1357
1406
|
opt: CommonDaoSaveBatchOptions<any> = {},
|
|
1358
1407
|
): Promise<void> {
|
|
1359
1408
|
if (!inputs.length) return
|
|
@@ -1365,7 +1414,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, I
|
|
|
1365
1414
|
dbmsByTable[table] = await dao.bmsToDBM(rows, opt)
|
|
1366
1415
|
})
|
|
1367
1416
|
|
|
1368
|
-
await db.
|
|
1417
|
+
await db.multiSave(dbmsByTable)
|
|
1369
1418
|
}
|
|
1370
1419
|
|
|
1371
1420
|
async createTransaction(opt?: CommonDBTransactionOptions): Promise<CommonDaoTransaction> {
|
|
@@ -1506,7 +1555,7 @@ export class CommonDaoTransaction {
|
|
|
1506
1555
|
}
|
|
1507
1556
|
}
|
|
1508
1557
|
|
|
1509
|
-
async getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
|
|
1558
|
+
async getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID extends string = BM['id']>(
|
|
1510
1559
|
dao: CommonDao<BM, DBM, ID>,
|
|
1511
1560
|
id?: ID | null,
|
|
1512
1561
|
opt?: CommonDaoReadOptions,
|
|
@@ -1514,7 +1563,7 @@ export class CommonDaoTransaction {
|
|
|
1514
1563
|
return await dao.getById(id, { ...opt, tx: this.tx })
|
|
1515
1564
|
}
|
|
1516
1565
|
|
|
1517
|
-
async getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
|
|
1566
|
+
async getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID extends string = BM['id']>(
|
|
1518
1567
|
dao: CommonDao<BM, DBM, ID>,
|
|
1519
1568
|
ids: ID[],
|
|
1520
1569
|
opt?: CommonDaoReadOptions,
|
|
@@ -1559,7 +1608,7 @@ export class CommonDaoTransaction {
|
|
|
1559
1608
|
*
|
|
1560
1609
|
* So, this method is a rather simple convenience "Object.assign and then save".
|
|
1561
1610
|
*/
|
|
1562
|
-
async patch<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
|
|
1611
|
+
async patch<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID extends string = BM['id']>(
|
|
1563
1612
|
dao: CommonDao<BM, DBM, ID>,
|
|
1564
1613
|
bm: BM,
|
|
1565
1614
|
patch: Partial<BM>,
|
|
@@ -1570,20 +1619,42 @@ export class CommonDaoTransaction {
|
|
|
1570
1619
|
return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx })
|
|
1571
1620
|
}
|
|
1572
1621
|
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1622
|
+
// todo: use AnyDao/Infer in other methods as well, if this works well
|
|
1623
|
+
async deleteById<DAO extends AnyDao>(
|
|
1624
|
+
dao: DAO,
|
|
1625
|
+
id?: InferID<DAO> | null,
|
|
1576
1626
|
opt?: CommonDaoOptions,
|
|
1577
1627
|
): Promise<number> {
|
|
1578
1628
|
if (!id) return 0
|
|
1579
1629
|
return await this.deleteByIds(dao, [id], opt)
|
|
1580
1630
|
}
|
|
1581
1631
|
|
|
1582
|
-
async deleteByIds<
|
|
1583
|
-
dao:
|
|
1584
|
-
ids:
|
|
1632
|
+
async deleteByIds<DAO extends AnyDao>(
|
|
1633
|
+
dao: DAO,
|
|
1634
|
+
ids: InferID<DAO>[],
|
|
1585
1635
|
opt?: CommonDaoOptions,
|
|
1586
1636
|
): Promise<number> {
|
|
1587
1637
|
return await dao.deleteByIds(ids, { ...opt, tx: this.tx })
|
|
1588
1638
|
}
|
|
1589
1639
|
}
|
|
1640
|
+
|
|
1641
|
+
export interface DaoWithIds<DAO extends AnyDao> {
|
|
1642
|
+
dao: DAO
|
|
1643
|
+
ids: string[]
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
export interface DaoWithId<DAO extends AnyDao> {
|
|
1647
|
+
dao: DAO
|
|
1648
|
+
id: string
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
export interface DaoWithRows<DAO extends AnyDao> {
|
|
1652
|
+
dao: DAO
|
|
1653
|
+
rows: InferBM<DAO>[]
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
type InferBM<DAO> = DAO extends CommonDao<infer BM> ? BM : never
|
|
1657
|
+
// type InferDBM<DAO> = DAO extends CommonDao<any, infer DBM> ? DBM : never
|
|
1658
|
+
type InferID<DAO> = DAO extends CommonDao<any, any, infer ID> ? ID : never
|
|
1659
|
+
|
|
1660
|
+
export type AnyDao = CommonDao<any>
|
|
@@ -113,21 +113,21 @@ export class BaseCommonDB implements CommonDB {
|
|
|
113
113
|
throw new Error('incrementBatch is not implemented')
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
async
|
|
116
|
+
async multiGet<ROW extends ObjectWithId>(
|
|
117
117
|
_map: StringMap<string[]>,
|
|
118
118
|
_opt?: CommonDBReadOptions,
|
|
119
119
|
): Promise<StringMap<ROW[]>> {
|
|
120
120
|
throw new Error('multiGetByIds is not implemented')
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
async
|
|
123
|
+
async multiSave<ROW extends ObjectWithId>(
|
|
124
124
|
_map: StringMap<ROW[]>,
|
|
125
125
|
_opt?: CommonDBSaveOptions<ROW>,
|
|
126
126
|
): Promise<void> {
|
|
127
127
|
throw new Error('multiSaveBatch is not implemented')
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
async
|
|
130
|
+
async multiDelete(_map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<number> {
|
|
131
131
|
throw new Error('multiDeleteByIds is not implemented')
|
|
132
132
|
}
|
|
133
133
|
}
|
|
@@ -14,16 +14,6 @@ import type {
|
|
|
14
14
|
} from '../db.model.js'
|
|
15
15
|
import type { DBQuery } from '../query/dbQuery.js'
|
|
16
16
|
|
|
17
|
-
export enum CommonDBType {
|
|
18
|
-
'document' = 'document',
|
|
19
|
-
'relational' = 'relational',
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* A tuple that contains the table name and the id.
|
|
24
|
-
*/
|
|
25
|
-
export type CommonDBKey = [table: string, id: string]
|
|
26
|
-
|
|
27
17
|
export interface CommonDB {
|
|
28
18
|
/**
|
|
29
19
|
* Relational databases are expected to return `null` for all missing properties.
|
|
@@ -93,7 +83,7 @@ export interface CommonDB {
|
|
|
93
83
|
*
|
|
94
84
|
* @experimental
|
|
95
85
|
*/
|
|
96
|
-
|
|
86
|
+
multiGet: <ROW extends ObjectWithId>(
|
|
97
87
|
idsByTable: StringMap<string[]>,
|
|
98
88
|
opt?: CommonDBReadOptions,
|
|
99
89
|
) => Promise<StringMap<ROW[]>>
|
|
@@ -140,7 +130,7 @@ export interface CommonDB {
|
|
|
140
130
|
*
|
|
141
131
|
* @experimental
|
|
142
132
|
*/
|
|
143
|
-
|
|
133
|
+
multiSave: <ROW extends ObjectWithId>(
|
|
144
134
|
rowsByTable: StringMap<ROW[]>,
|
|
145
135
|
opt?: CommonDBSaveOptions<ROW>,
|
|
146
136
|
) => Promise<void>
|
|
@@ -183,7 +173,7 @@ export interface CommonDB {
|
|
|
183
173
|
*
|
|
184
174
|
* @experimental
|
|
185
175
|
*/
|
|
186
|
-
|
|
176
|
+
multiDelete: (
|
|
187
177
|
idsByTable: StringMap<string[]>,
|
|
188
178
|
opt?: CommonDBOptions,
|
|
189
179
|
) => Promise<NonNegativeInteger>
|
|
@@ -258,6 +248,11 @@ export interface CommonDB {
|
|
|
258
248
|
) => Promise<StringMap<number>>
|
|
259
249
|
}
|
|
260
250
|
|
|
251
|
+
export enum CommonDBType {
|
|
252
|
+
'document' = 'document',
|
|
253
|
+
'relational' = 'relational',
|
|
254
|
+
}
|
|
255
|
+
|
|
261
256
|
/**
|
|
262
257
|
* Manifest of supported features.
|
|
263
258
|
*/
|
|
@@ -145,7 +145,7 @@ export class InMemoryDB implements CommonDB {
|
|
|
145
145
|
return ids.map(id => this.data[table]![id] as ROW).filter(Boolean)
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
async
|
|
148
|
+
async multiGet<ROW extends ObjectWithId>(
|
|
149
149
|
map: StringMap<string[]>,
|
|
150
150
|
_opt: CommonDBOptions = {},
|
|
151
151
|
): Promise<StringMap<ROW[]>> {
|
|
@@ -192,7 +192,7 @@ export class InMemoryDB implements CommonDB {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
async
|
|
195
|
+
async multiSave<ROW extends ObjectWithId>(
|
|
196
196
|
map: StringMap<ROW[]>,
|
|
197
197
|
opt: CommonDBSaveOptions<ROW> = {},
|
|
198
198
|
): Promise<void> {
|
|
@@ -239,7 +239,7 @@ export class InMemoryDB implements CommonDB {
|
|
|
239
239
|
return count
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
async
|
|
242
|
+
async multiDelete(map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<number> {
|
|
243
243
|
let count = 0
|
|
244
244
|
|
|
245
245
|
for (const [table, ids] of _stringMapEntries(map)) {
|
package/src/query/dbQuery.ts
CHANGED
|
@@ -236,7 +236,7 @@ export class DBQuery<ROW extends ObjectWithId> {
|
|
|
236
236
|
export class RunnableDBQuery<
|
|
237
237
|
BM extends BaseDBEntity,
|
|
238
238
|
DBM extends BaseDBEntity = BM,
|
|
239
|
-
ID = BM['id'],
|
|
239
|
+
ID extends string = BM['id'],
|
|
240
240
|
> extends DBQuery<DBM> {
|
|
241
241
|
/**
|
|
242
242
|
* Pass `table` to override table.
|