@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.
@@ -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 { AsyncIndexedMapper, BaseDBEntity, NonNegativeInteger, StringMap, UnixTimestampMillis, Unsaved } from '@naturalcycles/js-lib/types';
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, DaoWithId, DaoWithIds, DaoWithRows } from './common.dao.model.js';
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
- id(id: string): DaoWithId;
171
- ids(ids: string[]): DaoWithIds;
172
- rows(rows: BM[]): DaoWithRows;
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
- * Very @experimental.
175
- */
176
- static multiGetById<T>(inputs: DaoWithId[], opt?: CommonDaoReadOptions): Promise<T>;
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 multiGetByIds(inputs: DaoWithIds[], opt?: CommonDaoReadOptions): Promise<StringMap<unknown[]>>;
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 multiSaveBatch(inputs: DaoWithRows[], opt?: CommonDaoSaveBatchOptions<any>): Promise<void>;
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<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoOptions): Promise<number>;
234
- deleteByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoOptions): Promise<number>;
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
- id(id) {
985
+ withId(id) {
985
986
  return {
986
987
  dao: this,
987
988
  id,
988
989
  };
989
990
  }
990
- ids(ids) {
991
+ withIds(ids) {
991
992
  return {
992
993
  dao: this,
993
994
  ids,
994
995
  };
995
996
  }
996
- rows(rows) {
997
+ toSave(input) {
997
998
  return {
998
999
  dao: this,
999
- rows,
1000
+ rows: [input].flat(),
1000
1001
  };
1001
1002
  }
1002
1003
  /**
1003
- * Very @experimental.
1004
- */
1005
- static async multiGetById(inputs, opt = {}) {
1006
- const inputs2 = inputs.map(input => ({
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 multiGetByIds(inputs, opt = {}) {
1021
- if (!inputs.length)
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.multiGetByIds(idsByTable, opt);
1031
- const bmsByTable = {};
1032
- await pMap(inputs, async ({ dao }) => {
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
- let dbms = dbmsByTable[table] || [];
1035
- if (dao.cfg.hooks.afterLoad && dbms.length) {
1036
- dbms = (await pMap(dbms, async (dbm) => await dao.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
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 bmsByTable;
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.multiDeleteByIds(idsByTable);
1087
+ return await db.multiDelete(idsByTable);
1054
1088
  }
1055
- static async multiSaveBatch(inputs, opt = {}) {
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.multiSaveBatch(dbmsByTable);
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, ObjectWithId, Promisable, UnixTimestamp } from '@naturalcycles/js-lib/types';
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
- multiGetByIds<ROW extends ObjectWithId>(_map: StringMap<string[]>, _opt?: CommonDBReadOptions): Promise<StringMap<ROW[]>>;
32
- multiSaveBatch<ROW extends ObjectWithId>(_map: StringMap<ROW[]>, _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
33
- multiDeleteByIds(_map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<number>;
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 multiGetByIds(_map, _opt) {
60
+ async multiGet(_map, _opt) {
61
61
  throw new Error('multiGetByIds is not implemented');
62
62
  }
63
- async multiSaveBatch(_map, _opt) {
63
+ async multiSave(_map, _opt) {
64
64
  throw new Error('multiSaveBatch is not implemented');
65
65
  }
66
- async multiDeleteByIds(_map, _opt) {
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
- multiGetByIds: <ROW extends ObjectWithId>(idsByTable: StringMap<string[]>, opt?: CommonDBReadOptions) => Promise<StringMap<ROW[]>>;
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
- multiSaveBatch: <ROW extends ObjectWithId>(rowsByTable: StringMap<ROW[]>, opt?: CommonDBSaveOptions<ROW>) => Promise<void>;
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
- multiDeleteByIds: (idsByTable: StringMap<string[]>, opt?: CommonDBOptions) => Promise<NonNegativeInteger>;
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
- multiGetByIds<ROW extends ObjectWithId>(map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<StringMap<ROW[]>>;
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
- multiSaveBatch<ROW extends ObjectWithId>(map: StringMap<ROW[]>, opt?: CommonDBSaveOptions<ROW>): Promise<void>;
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
- multiDeleteByIds(map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<number>;
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 multiGetByIds(map, _opt = {}) {
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 multiSaveBatch(map, opt = {}) {
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 multiDeleteByIds(map, _opt) {
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);
@@ -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,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/db-lib",
3
3
  "type": "module",
4
- "version": "10.18.0",
4
+ "version": "10.19.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@naturalcycles/nodejs-lib": "^15"
@@ -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 type {
18
- AsyncIndexedMapper,
19
- BaseDBEntity,
20
- NonNegativeInteger,
21
- ObjectWithId,
22
- StringMap,
23
- UnixTimestampMillis,
24
- Unsaved,
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<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> {
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
- id(id: string): DaoWithId {
1267
+ withId(id: ID): DaoWithId<typeof this> {
1265
1268
  return {
1266
1269
  dao: this,
1267
1270
  id,
1268
1271
  }
1269
1272
  }
1270
1273
 
1271
- ids(ids: string[]): DaoWithIds {
1274
+ withIds(ids: ID[]): DaoWithIds<typeof this> {
1272
1275
  return {
1273
1276
  dao: this,
1274
1277
  ids,
1275
1278
  }
1276
1279
  }
1277
1280
 
1278
- rows(rows: BM[]): DaoWithRows {
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
- * Very @experimental.
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 multiGetById<T>(inputs: DaoWithId[], opt: CommonDaoReadOptions = {}): Promise<T> {
1289
- const inputs2 = inputs.map(input => ({
1290
- dao: input.dao,
1291
- ids: [input.id],
1292
- }))
1293
-
1294
- const rowsByTable = await CommonDao.multiGetByIds(inputs2, opt)
1295
- const results: any[] = inputs.map(({ dao }) => {
1296
- const { table } = dao.cfg
1297
- return rowsByTable[table]?.[0] || null
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 results as T
1314
+ return (await CommonDao.prepareMultiGetOutput(inputMap, dbmByTableById, opt)) as any
1301
1315
  }
1302
1316
 
1303
- /**
1304
- * Very @experimental.
1305
- */
1306
- static async multiGetByIds(
1307
- inputs: DaoWithIds[],
1308
- opt: CommonDaoReadOptions = {},
1309
- ): Promise<StringMap<unknown[]>> {
1310
- if (!inputs.length) return {}
1311
- const { db } = inputs[0]!.dao.cfg
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 { dao, ids } of inputs) {
1314
- const { table } = dao.cfg
1315
- idsByTable[table] = ids
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
- // todo: support tx
1319
- const dbmsByTable = await db.multiGetByIds(idsByTable, opt)
1320
- const bmsByTable: StringMap<unknown[]> = {}
1357
+ return dbmByTableById
1358
+ }
1321
1359
 
1322
- await pMap(inputs, async ({ dao }) => {
1323
- const { table } = dao.cfg
1324
- let dbms = dbmsByTable[table] || []
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
- if (dao.cfg.hooks!.afterLoad && dbms.length) {
1327
- dbms = (await pMap(dbms, async dbm => await dao.cfg.hooks!.afterLoad!(dbm))).filter(
1328
- _isTruthy,
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 bmsByTable
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.multiDeleteByIds(idsByTable)
1401
+ return await db.multiDelete(idsByTable)
1353
1402
  }
1354
1403
 
1355
- static async multiSaveBatch(
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.multiSaveBatch(dbmsByTable)
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
- async deleteById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
1574
- dao: CommonDao<BM, DBM, ID>,
1575
- id?: ID | null,
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<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
1583
- dao: CommonDao<BM, DBM, ID>,
1584
- ids: ID[],
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 multiGetByIds<ROW extends ObjectWithId>(
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 multiSaveBatch<ROW extends ObjectWithId>(
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 multiDeleteByIds(_map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<number> {
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
- multiGetByIds: <ROW extends ObjectWithId>(
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
- multiSaveBatch: <ROW extends ObjectWithId>(
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
- multiDeleteByIds: (
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 multiGetByIds<ROW extends ObjectWithId>(
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 multiSaveBatch<ROW extends ObjectWithId>(
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 multiDeleteByIds(map: StringMap<string[]>, _opt?: CommonDBOptions): Promise<number> {
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)) {
@@ -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.