@naturalcycles/db-lib 9.15.1 → 9.17.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/adapter/inmemory/queryInMemory.js +2 -1
- package/dist/base.common.db.d.ts +11 -11
- package/dist/base.common.db.js +11 -12
- package/dist/commondao/common.dao.d.ts +29 -25
- package/dist/commondao/common.dao.js +27 -1
- package/dist/commondao/common.dao.model.d.ts +6 -6
- package/dist/query/dbQuery.d.ts +6 -6
- package/dist/testing/daoTest.js +0 -1
- package/dist/testing/dbTest.js +3 -4
- package/dist/testing/keyValueDBTest.js +0 -1
- package/dist/testing/keyValueDaoTest.js +0 -1
- package/dist/transaction/dbTransaction.util.d.ts +1 -1
- package/package.json +1 -1
- package/src/adapter/inmemory/queryInMemory.ts +2 -1
- package/src/base.common.db.ts +16 -18
- package/src/commondao/common.dao.model.ts +10 -6
- package/src/commondao/common.dao.ts +86 -55
- package/src/query/dbQuery.ts +7 -6
- package/src/testing/daoTest.ts +0 -1
- package/src/testing/dbTest.ts +4 -7
- package/src/testing/keyValueDBTest.ts +0 -2
- package/src/testing/keyValueDaoTest.ts +0 -2
- package/src/transaction/dbTransaction.util.ts +1 -5
- package/dist/testing/test.util.d.ts +0 -6
- package/dist/testing/test.util.js +0 -23
- package/src/testing/test.util.ts +0 -24
|
@@ -31,8 +31,9 @@ function queryInMemory(q, rows = []) {
|
|
|
31
31
|
if (order) {
|
|
32
32
|
const { name, descending } = order;
|
|
33
33
|
rows = rows.sort((a, b) => {
|
|
34
|
+
// biome-ignore lint/suspicious/noDoubleEquals: ok
|
|
34
35
|
if (a[name] == b[name])
|
|
35
|
-
return 0;
|
|
36
|
+
return 0;
|
|
36
37
|
if (descending) {
|
|
37
38
|
return a[name] < b[name] ? 1 : -1;
|
|
38
39
|
}
|
package/dist/base.common.db.d.ts
CHANGED
|
@@ -12,15 +12,15 @@ export declare class BaseCommonDB implements CommonDB {
|
|
|
12
12
|
support: CommonDBSupport;
|
|
13
13
|
ping(): Promise<void>;
|
|
14
14
|
getTables(): Promise<string[]>;
|
|
15
|
-
getTableSchema<ROW extends ObjectWithId>(
|
|
16
|
-
createTable<ROW extends ObjectWithId>(
|
|
17
|
-
getByIds<ROW extends ObjectWithId>(
|
|
18
|
-
deleteByQuery<ROW extends ObjectWithId>(
|
|
19
|
-
updateByQuery<ROW extends ObjectWithId>(
|
|
20
|
-
runQuery<ROW extends ObjectWithId>(
|
|
21
|
-
runQueryCount<ROW extends ObjectWithId>(
|
|
22
|
-
saveBatch<ROW extends ObjectWithId>(
|
|
23
|
-
streamQuery<ROW extends ObjectWithId>(
|
|
24
|
-
deleteByIds(
|
|
25
|
-
runInTransaction(fn: DBTransactionFn,
|
|
15
|
+
getTableSchema<ROW extends ObjectWithId>(_table: string): Promise<JsonSchemaRootObject<ROW>>;
|
|
16
|
+
createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>): Promise<void>;
|
|
17
|
+
getByIds<ROW extends ObjectWithId>(_table: string, _ids: string[]): Promise<ROW[]>;
|
|
18
|
+
deleteByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
|
|
19
|
+
updateByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>, _patch: DBPatch<ROW>, _opt?: CommonDBOptions): Promise<number>;
|
|
20
|
+
runQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<RunQueryResult<ROW>>;
|
|
21
|
+
runQueryCount<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
|
|
22
|
+
saveBatch<ROW extends ObjectWithId>(_table: string, _rows: ROW[], _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
23
|
+
streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW>;
|
|
24
|
+
deleteByIds(_table: string, _ids: string[], _opt?: CommonDBOptions): Promise<number>;
|
|
25
|
+
runInTransaction(fn: DBTransactionFn, _opt?: CommonDBTransactionOptions): Promise<void>;
|
|
26
26
|
}
|
package/dist/base.common.db.js
CHANGED
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BaseCommonDB = void 0;
|
|
4
4
|
const common_db_1 = require("./common.db");
|
|
5
5
|
const dbTransaction_util_1 = require("./transaction/dbTransaction.util");
|
|
6
|
-
/* eslint-disable unused-imports/no-unused-vars */
|
|
7
6
|
/**
|
|
8
7
|
* No-op implementation of CommonDB interface.
|
|
9
8
|
* To be extended by actual implementations.
|
|
@@ -19,37 +18,37 @@ class BaseCommonDB {
|
|
|
19
18
|
async getTables() {
|
|
20
19
|
throw new Error('getTables is not implemented');
|
|
21
20
|
}
|
|
22
|
-
async getTableSchema(
|
|
21
|
+
async getTableSchema(_table) {
|
|
23
22
|
throw new Error('getTableSchema is not implemented');
|
|
24
23
|
}
|
|
25
|
-
async createTable(
|
|
24
|
+
async createTable(_table, _schema) {
|
|
26
25
|
// no-op
|
|
27
26
|
}
|
|
28
|
-
async getByIds(
|
|
27
|
+
async getByIds(_table, _ids) {
|
|
29
28
|
throw new Error('getByIds is not implemented');
|
|
30
29
|
}
|
|
31
|
-
async deleteByQuery(
|
|
30
|
+
async deleteByQuery(_q) {
|
|
32
31
|
throw new Error('deleteByQuery is not implemented');
|
|
33
32
|
}
|
|
34
|
-
async updateByQuery(
|
|
33
|
+
async updateByQuery(_q, _patch, _opt) {
|
|
35
34
|
throw new Error('updateByQuery is not implemented');
|
|
36
35
|
}
|
|
37
|
-
async runQuery(
|
|
36
|
+
async runQuery(_q) {
|
|
38
37
|
throw new Error('runQuery is not implemented');
|
|
39
38
|
}
|
|
40
|
-
async runQueryCount(
|
|
39
|
+
async runQueryCount(_q) {
|
|
41
40
|
throw new Error('runQueryCount is not implemented');
|
|
42
41
|
}
|
|
43
|
-
async saveBatch(
|
|
42
|
+
async saveBatch(_table, _rows, _opt) {
|
|
44
43
|
throw new Error('saveBatch is not implemented');
|
|
45
44
|
}
|
|
46
|
-
streamQuery(
|
|
45
|
+
streamQuery(_q) {
|
|
47
46
|
throw new Error('streamQuery is not implemented');
|
|
48
47
|
}
|
|
49
|
-
async deleteByIds(
|
|
48
|
+
async deleteByIds(_table, _ids, _opt) {
|
|
50
49
|
throw new Error('deleteByIds is not implemented');
|
|
51
50
|
}
|
|
52
|
-
async runInTransaction(fn,
|
|
51
|
+
async runInTransaction(fn, _opt) {
|
|
53
52
|
const tx = new dbTransaction_util_1.FakeDBTransaction(this);
|
|
54
53
|
await fn(tx);
|
|
55
54
|
// there's no try/catch and rollback, as there's nothing to rollback
|
|
@@ -11,19 +11,19 @@ import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoPatchB
|
|
|
11
11
|
* BM = Backend model (optimized for API access)
|
|
12
12
|
* TM = Transport model (optimized to be sent over the wire)
|
|
13
13
|
*/
|
|
14
|
-
export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
15
|
-
cfg: CommonDaoCfg<BM, DBM>;
|
|
16
|
-
constructor(cfg: CommonDaoCfg<BM, DBM>);
|
|
14
|
+
export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> {
|
|
15
|
+
cfg: CommonDaoCfg<BM, DBM, ID>;
|
|
16
|
+
constructor(cfg: CommonDaoCfg<BM, DBM, ID>);
|
|
17
17
|
create(part?: Partial<BM>, opt?: CommonDaoOptions): BM;
|
|
18
18
|
getById(id: undefined | null, opt?: CommonDaoOptions): Promise<null>;
|
|
19
|
-
getById(id?:
|
|
20
|
-
getByIdOrEmpty(id:
|
|
19
|
+
getById(id?: ID | null, opt?: CommonDaoOptions): Promise<BM | null>;
|
|
20
|
+
getByIdOrEmpty(id: ID, part?: Partial<BM>, opt?: CommonDaoOptions): Promise<BM>;
|
|
21
21
|
getByIdAsDBM(id: undefined | null, opt?: CommonDaoOptions): Promise<null>;
|
|
22
|
-
getByIdAsDBM(id?:
|
|
23
|
-
getByIds(ids:
|
|
24
|
-
getByIdsAsDBM(ids:
|
|
25
|
-
requireById(id:
|
|
26
|
-
requireByIdAsDBM(id:
|
|
22
|
+
getByIdAsDBM(id?: ID | null, opt?: CommonDaoOptions): Promise<DBM | null>;
|
|
23
|
+
getByIds(ids: ID[], opt?: CommonDaoOptions): Promise<BM[]>;
|
|
24
|
+
getByIdsAsDBM(ids: ID[], opt?: CommonDaoOptions): Promise<DBM[]>;
|
|
25
|
+
requireById(id: ID, opt?: CommonDaoOptions): Promise<BM>;
|
|
26
|
+
requireByIdAsDBM(id: ID, opt?: CommonDaoOptions): Promise<DBM>;
|
|
27
27
|
private throwRequiredError;
|
|
28
28
|
/**
|
|
29
29
|
* Throws if readOnly is true
|
|
@@ -40,7 +40,7 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
40
40
|
/**
|
|
41
41
|
* Pass `table` to override table
|
|
42
42
|
*/
|
|
43
|
-
query(table?: string): RunnableDBQuery<BM, DBM>;
|
|
43
|
+
query(table?: string): RunnableDBQuery<BM, DBM, ID>;
|
|
44
44
|
runQuery(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<BM[]>;
|
|
45
45
|
runQuerySingleColumn<T = any>(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<T[]>;
|
|
46
46
|
/**
|
|
@@ -69,9 +69,9 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
69
69
|
* You can do `.pipe(transformNoOp)` to make it "valid again".
|
|
70
70
|
*/
|
|
71
71
|
streamQuery(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<BM>): ReadableTyped<BM>;
|
|
72
|
-
queryIds(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<
|
|
73
|
-
streamQueryIds(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<
|
|
74
|
-
streamQueryIdsForEach(q: DBQuery<DBM>, mapper: AsyncMapper<
|
|
72
|
+
queryIds(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<ID[]>;
|
|
73
|
+
streamQueryIds(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<ID>): ReadableTyped<ID>;
|
|
74
|
+
streamQueryIdsForEach(q: DBQuery<DBM>, mapper: AsyncMapper<ID, void>, opt?: CommonDaoStreamForEachOptions<ID>): Promise<void>;
|
|
75
75
|
/**
|
|
76
76
|
* Mutates!
|
|
77
77
|
* "Returns", just to have a type of "Saved"
|
|
@@ -86,11 +86,11 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
86
86
|
* 2. Applies the patch on top of loaded data.
|
|
87
87
|
* 3. Saves (as fast as possible since the read) with the Patch applied, but only if the data has changed.
|
|
88
88
|
*/
|
|
89
|
-
patchById(id:
|
|
89
|
+
patchById(id: ID, patch: Partial<BM>, opt?: CommonDaoPatchByIdOptions<DBM>): Promise<BM>;
|
|
90
90
|
/**
|
|
91
91
|
* Like patchById, but runs all operations within a Transaction.
|
|
92
92
|
*/
|
|
93
|
-
patchByIdInTransaction(id:
|
|
93
|
+
patchByIdInTransaction(id: ID, patch: Partial<BM>, opt?: CommonDaoPatchByIdOptions<DBM>): Promise<BM>;
|
|
94
94
|
/**
|
|
95
95
|
* Same as patchById, but takes the whole object as input.
|
|
96
96
|
* This "whole object" is mutated with the patch and returned.
|
|
@@ -119,16 +119,16 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
119
119
|
/**
|
|
120
120
|
* @returns number of deleted items
|
|
121
121
|
*/
|
|
122
|
-
deleteById(id?:
|
|
123
|
-
deleteByIds(ids:
|
|
122
|
+
deleteById(id?: ID | null, opt?: CommonDaoOptions): Promise<number>;
|
|
123
|
+
deleteByIds(ids: ID[], opt?: CommonDaoOptions): Promise<number>;
|
|
124
124
|
/**
|
|
125
125
|
* Pass `chunkSize: number` (e.g 500) option to use Streaming: it will Stream the query, chunk by 500, and execute
|
|
126
126
|
* `deleteByIds` for each chunk concurrently (infinite concurrency).
|
|
127
127
|
* This is expected to be more memory-efficient way of deleting large number of rows.
|
|
128
128
|
*/
|
|
129
129
|
deleteByQuery(q: DBQuery<DBM>, opt?: CommonDaoStreamDeleteOptions<DBM>): Promise<number>;
|
|
130
|
-
updateById(id:
|
|
131
|
-
updateByIds(ids:
|
|
130
|
+
updateById(id: ID, patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
|
|
131
|
+
updateByIds(ids: ID[], patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
|
|
132
132
|
updateByQuery(q: DBQuery<DBM>, patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
|
|
133
133
|
dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): Promise<undefined>;
|
|
134
134
|
dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): Promise<BM>;
|
|
@@ -157,6 +157,10 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
157
157
|
*/
|
|
158
158
|
ping(): Promise<void>;
|
|
159
159
|
runInTransaction<T = void>(fn: CommonDaoTransactionFn<T>, opt?: CommonDBTransactionOptions): Promise<T>;
|
|
160
|
+
/**
|
|
161
|
+
* Throws if query uses a property that is in `excludeFromIndexes` list.
|
|
162
|
+
*/
|
|
163
|
+
private validateQueryIndexes;
|
|
160
164
|
protected logResult(started: number, op: string, res: any, table: string): void;
|
|
161
165
|
protected logSaveResult(started: number, op: string, table: string): void;
|
|
162
166
|
protected logStarted(op: string, table: string, force?: boolean): UnixTimestampMillisNumber;
|
|
@@ -180,8 +184,8 @@ export declare class CommonDaoTransaction {
|
|
|
180
184
|
* Perform a graceful rollback without throwing/re-throwing any error.
|
|
181
185
|
*/
|
|
182
186
|
rollback(): Promise<void>;
|
|
183
|
-
getById<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, id?:
|
|
184
|
-
getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, ids:
|
|
187
|
+
getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoOptions): Promise<BM | null>;
|
|
188
|
+
getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoOptions): Promise<BM[]>;
|
|
185
189
|
save<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
|
|
186
190
|
saveBatch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM[]>;
|
|
187
191
|
/**
|
|
@@ -191,7 +195,7 @@ export declare class CommonDaoTransaction {
|
|
|
191
195
|
*
|
|
192
196
|
* So, this method is a rather simple convenience "Object.assign and then save".
|
|
193
197
|
*/
|
|
194
|
-
patch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
|
|
195
|
-
deleteById(dao: CommonDao<
|
|
196
|
-
deleteByIds(dao: CommonDao<
|
|
198
|
+
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>;
|
|
199
|
+
deleteById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, id?: ID | null, opt?: CommonDaoOptions): Promise<number>;
|
|
200
|
+
deleteByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(dao: CommonDao<BM, DBM, ID>, ids: ID[], opt?: CommonDaoOptions): Promise<number>;
|
|
197
201
|
}
|
|
@@ -202,6 +202,7 @@ class CommonDao {
|
|
|
202
202
|
return (0, js_lib_1._uniqBy)(results, r => r.id);
|
|
203
203
|
}
|
|
204
204
|
async runQueryExtended(q, opt = {}) {
|
|
205
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
205
206
|
q.table = opt.table || q.table;
|
|
206
207
|
const op = `runQuery(${q.pretty()})`;
|
|
207
208
|
const started = this.logStarted(op, q.table);
|
|
@@ -222,6 +223,7 @@ class CommonDao {
|
|
|
222
223
|
return rows;
|
|
223
224
|
}
|
|
224
225
|
async runQueryExtendedAsDBM(q, opt = {}) {
|
|
226
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
225
227
|
q.table = opt.table || q.table;
|
|
226
228
|
const op = `runQueryAsDBM(${q.pretty()})`;
|
|
227
229
|
const started = this.logStarted(op, q.table);
|
|
@@ -235,6 +237,7 @@ class CommonDao {
|
|
|
235
237
|
return { rows: dbms, ...queryResult };
|
|
236
238
|
}
|
|
237
239
|
async runQueryCount(q, opt = {}) {
|
|
240
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
238
241
|
q.table = opt.table || q.table;
|
|
239
242
|
const op = `runQueryCount(${q.pretty()})`;
|
|
240
243
|
const started = this.logStarted(op, q.table);
|
|
@@ -245,6 +248,7 @@ class CommonDao {
|
|
|
245
248
|
return count;
|
|
246
249
|
}
|
|
247
250
|
async streamQueryForEach(q, mapper, opt = {}) {
|
|
251
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
248
252
|
q.table = opt.table || q.table;
|
|
249
253
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
250
254
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
@@ -283,6 +287,7 @@ class CommonDao {
|
|
|
283
287
|
}
|
|
284
288
|
}
|
|
285
289
|
async streamQueryAsDBMForEach(q, mapper, opt = {}) {
|
|
290
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
286
291
|
q.table = opt.table || q.table;
|
|
287
292
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
288
293
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
@@ -324,6 +329,7 @@ class CommonDao {
|
|
|
324
329
|
* Stream as Readable, to be able to .pipe() it further with support of backpressure.
|
|
325
330
|
*/
|
|
326
331
|
streamQueryAsDBM(q, opt = {}) {
|
|
332
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
327
333
|
q.table = opt.table || q.table;
|
|
328
334
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
329
335
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
@@ -354,6 +360,7 @@ class CommonDao {
|
|
|
354
360
|
* You can do `.pipe(transformNoOp)` to make it "valid again".
|
|
355
361
|
*/
|
|
356
362
|
streamQuery(q, opt = {}) {
|
|
363
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
357
364
|
q.table = opt.table || q.table;
|
|
358
365
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
359
366
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
@@ -394,11 +401,13 @@ class CommonDao {
|
|
|
394
401
|
.pipe((0, nodejs_lib_1.transformNoOp)()));
|
|
395
402
|
}
|
|
396
403
|
async queryIds(q, opt = {}) {
|
|
404
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
397
405
|
q.table = opt.table || q.table;
|
|
398
406
|
const { rows } = await this.cfg.db.runQuery(q.select(['id']), opt);
|
|
399
407
|
return rows.map(r => r.id);
|
|
400
408
|
}
|
|
401
409
|
streamQueryIds(q, opt = {}) {
|
|
410
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
402
411
|
q.table = opt.table || q.table;
|
|
403
412
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
404
413
|
// Experimental: using `.map()`
|
|
@@ -417,6 +426,7 @@ class CommonDao {
|
|
|
417
426
|
return stream;
|
|
418
427
|
}
|
|
419
428
|
async streamQueryIdsForEach(q, mapper, opt = {}) {
|
|
429
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
420
430
|
q.table = opt.table || q.table;
|
|
421
431
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
422
432
|
const op = `streamQueryIdsForEach(${q.pretty()})`;
|
|
@@ -455,7 +465,8 @@ class CommonDao {
|
|
|
455
465
|
obj.updated = opt.preserveUpdatedCreated && obj.updated ? obj.updated : now;
|
|
456
466
|
}
|
|
457
467
|
if (this.cfg.generateId) {
|
|
458
|
-
obj.id ||= this.cfg.hooks.createNaturalId?.(obj) ||
|
|
468
|
+
obj.id ||= (this.cfg.hooks.createNaturalId?.(obj) ||
|
|
469
|
+
this.cfg.hooks.createRandomId());
|
|
459
470
|
}
|
|
460
471
|
return obj;
|
|
461
472
|
}
|
|
@@ -777,6 +788,7 @@ class CommonDao {
|
|
|
777
788
|
* This is expected to be more memory-efficient way of deleting large number of rows.
|
|
778
789
|
*/
|
|
779
790
|
async deleteByQuery(q, opt = {}) {
|
|
791
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
780
792
|
this.requireWriteAccess();
|
|
781
793
|
this.requireObjectMutability(opt);
|
|
782
794
|
q.table = opt.table || q.table;
|
|
@@ -820,6 +832,7 @@ class CommonDao {
|
|
|
820
832
|
return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt);
|
|
821
833
|
}
|
|
822
834
|
async updateByQuery(q, patch, opt = {}) {
|
|
835
|
+
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
823
836
|
this.requireWriteAccess();
|
|
824
837
|
this.requireObjectMutability(opt);
|
|
825
838
|
q.table = opt.table || q.table;
|
|
@@ -959,6 +972,19 @@ class CommonDao {
|
|
|
959
972
|
}, opt);
|
|
960
973
|
return r;
|
|
961
974
|
}
|
|
975
|
+
/**
|
|
976
|
+
* Throws if query uses a property that is in `excludeFromIndexes` list.
|
|
977
|
+
*/
|
|
978
|
+
validateQueryIndexes(q) {
|
|
979
|
+
const { excludeFromIndexes } = this.cfg;
|
|
980
|
+
if (!excludeFromIndexes)
|
|
981
|
+
return;
|
|
982
|
+
for (const f of q._filters) {
|
|
983
|
+
(0, js_lib_1._assert)(!excludeFromIndexes.includes(f.name), `cannot query on non-indexed property: ${this.cfg.table}.${f.name}`, {
|
|
984
|
+
query: q.pretty(),
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
}
|
|
962
988
|
logResult(started, op, res, table) {
|
|
963
989
|
if (!this.cfg.logLevel)
|
|
964
990
|
return;
|
|
@@ -2,24 +2,24 @@ import { BaseDBEntity, CommonLogger, ErrorMode, Promisable, ZodError, ZodSchema
|
|
|
2
2
|
import { AjvSchema, AjvValidationError, JoiValidationError, ObjectSchema, TransformLogProgressOptions, TransformMapOptions } from '@naturalcycles/nodejs-lib';
|
|
3
3
|
import { CommonDB } from '../common.db';
|
|
4
4
|
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model';
|
|
5
|
-
export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity> {
|
|
5
|
+
export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']> {
|
|
6
6
|
/**
|
|
7
7
|
* Allows to override the id generation function.
|
|
8
8
|
* By default it uses `stringId` from nodejs-lib
|
|
9
9
|
* (which uses lowercase alphanumberic alphabet and the size of 16).
|
|
10
10
|
*/
|
|
11
|
-
createRandomId: () =>
|
|
11
|
+
createRandomId: () => ID;
|
|
12
12
|
/**
|
|
13
13
|
* createNaturalId hook is called (tried) first.
|
|
14
14
|
* If it doesn't exist - createRandomId is called.
|
|
15
15
|
*/
|
|
16
|
-
createNaturalId: (obj: DBM | BM) =>
|
|
16
|
+
createNaturalId: (obj: DBM | BM) => ID;
|
|
17
17
|
/**
|
|
18
18
|
* It's a counter-part of `createNaturalId`.
|
|
19
19
|
* Allows to provide a parser function to parse "natural id" into
|
|
20
20
|
* DBM components (e.g accountId and some other property that is part of the id).
|
|
21
21
|
*/
|
|
22
|
-
parseNaturalId: (id:
|
|
22
|
+
parseNaturalId: (id: ID) => Partial<DBM>;
|
|
23
23
|
/**
|
|
24
24
|
* It is called only on `dao.create` method.
|
|
25
25
|
* Dao.create method is called in:
|
|
@@ -93,7 +93,7 @@ export declare enum CommonDaoLogLevel {
|
|
|
93
93
|
*/
|
|
94
94
|
DATA_FULL = 30
|
|
95
95
|
}
|
|
96
|
-
export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
96
|
+
export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> {
|
|
97
97
|
db: CommonDB;
|
|
98
98
|
table: string;
|
|
99
99
|
/**
|
|
@@ -138,7 +138,7 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
138
138
|
* @default false
|
|
139
139
|
*/
|
|
140
140
|
logStarted?: boolean;
|
|
141
|
-
hooks?: Partial<CommonDaoHooks<BM, DBM>>;
|
|
141
|
+
hooks?: Partial<CommonDaoHooks<BM, DBM, ID>>;
|
|
142
142
|
/**
|
|
143
143
|
* Defaults to true.
|
|
144
144
|
* Set to false to disable auto-generation of `id`.
|
package/dist/query/dbQuery.d.ts
CHANGED
|
@@ -87,12 +87,12 @@ 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> extends DBQuery<DBM> {
|
|
91
|
-
dao: CommonDao<BM, DBM>;
|
|
90
|
+
export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> extends DBQuery<DBM> {
|
|
91
|
+
dao: CommonDao<BM, DBM, ID>;
|
|
92
92
|
/**
|
|
93
93
|
* Pass `table` to override table.
|
|
94
94
|
*/
|
|
95
|
-
constructor(dao: CommonDao<BM, DBM>, table?: string);
|
|
95
|
+
constructor(dao: CommonDao<BM, DBM, ID>, table?: string);
|
|
96
96
|
runQuery(opt?: CommonDaoOptions): Promise<BM[]>;
|
|
97
97
|
runQuerySingleColumn<T = any>(opt?: CommonDaoOptions): Promise<T[]>;
|
|
98
98
|
runQueryAsDBM(opt?: CommonDaoOptions): Promise<DBM[]>;
|
|
@@ -104,8 +104,8 @@ export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDB
|
|
|
104
104
|
streamQueryAsDBMForEach(mapper: AsyncMapper<DBM, void>, opt?: CommonDaoStreamForEachOptions<DBM>): Promise<void>;
|
|
105
105
|
streamQuery(opt?: CommonDaoStreamOptions<BM>): ReadableTyped<BM>;
|
|
106
106
|
streamQueryAsDBM(opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<DBM>;
|
|
107
|
-
queryIds(opt?: CommonDaoOptions): Promise<
|
|
108
|
-
streamQueryIds(opt?: CommonDaoStreamOptions<
|
|
109
|
-
streamQueryIdsForEach(mapper: AsyncMapper<
|
|
107
|
+
queryIds(opt?: CommonDaoOptions): Promise<ID[]>;
|
|
108
|
+
streamQueryIds(opt?: CommonDaoStreamOptions<ID>): ReadableTyped<ID>;
|
|
109
|
+
streamQueryIdsForEach(mapper: AsyncMapper<ID, void>, opt?: CommonDaoStreamForEachOptions<ID>): Promise<void>;
|
|
110
110
|
deleteByQuery(opt?: CommonDaoStreamDeleteOptions<DBM>): Promise<number>;
|
|
111
111
|
}
|
package/dist/testing/daoTest.js
CHANGED
|
@@ -8,7 +8,6 @@ const __1 = require("..");
|
|
|
8
8
|
const common_dao_1 = require("../commondao/common.dao");
|
|
9
9
|
const dbTest_1 = require("./dbTest");
|
|
10
10
|
const test_model_1 = require("./test.model");
|
|
11
|
-
// eslint-disable-next-line jest/no-export
|
|
12
11
|
function runCommonDaoTest(db, quirks = {}) {
|
|
13
12
|
const { support } = db;
|
|
14
13
|
const dao = new common_dao_1.CommonDao({
|
package/dist/testing/dbTest.js
CHANGED
|
@@ -7,11 +7,10 @@ const common_db_1 = require("../common.db");
|
|
|
7
7
|
const db_model_1 = require("../db.model");
|
|
8
8
|
const dbQuery_1 = require("../query/dbQuery");
|
|
9
9
|
const test_model_1 = require("./test.model");
|
|
10
|
-
const test_util_1 = require("./test.util");
|
|
11
10
|
function runCommonDBTest(db, quirks = {}) {
|
|
12
11
|
const { support } = db;
|
|
13
12
|
const items = (0, test_model_1.createTestItemsDBM)(3);
|
|
14
|
-
(0,
|
|
13
|
+
(0, js_lib_1._deepFreeze)(items);
|
|
15
14
|
const item1 = items[0];
|
|
16
15
|
const queryAll = () => dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE);
|
|
17
16
|
test('ping', async () => {
|
|
@@ -54,7 +53,7 @@ function runCommonDBTest(db, quirks = {}) {
|
|
|
54
53
|
...(0, test_model_1.createTestItemDBM)(3),
|
|
55
54
|
k2: null,
|
|
56
55
|
};
|
|
57
|
-
(0,
|
|
56
|
+
(0, js_lib_1._deepFreeze)(item3);
|
|
58
57
|
await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
|
|
59
58
|
const item3Loaded = (await db.getByIds(test_model_1.TEST_TABLE, [item3.id]))[0];
|
|
60
59
|
expectMatch([item3], [item3Loaded], quirks);
|
|
@@ -67,7 +66,7 @@ function runCommonDBTest(db, quirks = {}) {
|
|
|
67
66
|
...(0, test_model_1.createTestItemDBM)(3),
|
|
68
67
|
k2: undefined,
|
|
69
68
|
};
|
|
70
|
-
(0,
|
|
69
|
+
(0, js_lib_1._deepFreeze)(item3);
|
|
71
70
|
const expected = { ...item3 };
|
|
72
71
|
delete expected.k2;
|
|
73
72
|
await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runCommonKeyValueDBTest = runCommonKeyValueDBTest;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const test_model_1 = require("./test.model");
|
|
6
|
-
/* eslint-disable jest/no-export */
|
|
7
6
|
const testIds = (0, js_lib_1._range)(1, 4).map(n => `id${n}`);
|
|
8
7
|
const testEntries = testIds.map(id => [id, Buffer.from(`${id}value`)]);
|
|
9
8
|
function runCommonKeyValueDBTest(db) {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runCommonKeyValueDaoTest = runCommonKeyValueDaoTest;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
|
-
/* eslint-disable jest/no-export */
|
|
6
5
|
const testIds = (0, js_lib_1._range)(1, 4).map(n => `id${n}`);
|
|
7
6
|
const testEntries = testIds.map(id => [id, Buffer.from(`${id}value`)]);
|
|
8
7
|
function runCommonKeyValueDaoTest(dao) {
|
|
@@ -24,5 +24,5 @@ export declare class FakeDBTransaction implements DBTransaction {
|
|
|
24
24
|
rollback(): Promise<void>;
|
|
25
25
|
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: CommonDBOptions): Promise<ROW[]>;
|
|
26
26
|
saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
27
|
-
deleteByIds(table: string, ids: string[], opt?: CommonDBOptions
|
|
27
|
+
deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -35,7 +35,8 @@ export function queryInMemory<ROW extends ObjectWithId>(q: DBQuery<ROW>, rows: R
|
|
|
35
35
|
if (order) {
|
|
36
36
|
const { name, descending } = order
|
|
37
37
|
rows = rows.sort((a, b) => {
|
|
38
|
-
|
|
38
|
+
// biome-ignore lint/suspicious/noDoubleEquals: ok
|
|
39
|
+
if (a[name] == b[name]) return 0
|
|
39
40
|
|
|
40
41
|
if (descending) {
|
|
41
42
|
return a[name] < b[name] ? 1 : -1
|
package/src/base.common.db.ts
CHANGED
|
@@ -12,8 +12,6 @@ import {
|
|
|
12
12
|
import { DBQuery } from './query/dbQuery'
|
|
13
13
|
import { FakeDBTransaction } from './transaction/dbTransaction.util'
|
|
14
14
|
|
|
15
|
-
/* eslint-disable unused-imports/no-unused-vars */
|
|
16
|
-
|
|
17
15
|
/**
|
|
18
16
|
* No-op implementation of CommonDB interface.
|
|
19
17
|
* To be extended by actual implementations.
|
|
@@ -32,59 +30,59 @@ export class BaseCommonDB implements CommonDB {
|
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
async getTableSchema<ROW extends ObjectWithId>(
|
|
35
|
-
|
|
33
|
+
_table: string,
|
|
36
34
|
): Promise<JsonSchemaRootObject<ROW>> {
|
|
37
35
|
throw new Error('getTableSchema is not implemented')
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
async createTable<ROW extends ObjectWithId>(
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
_table: string,
|
|
40
|
+
_schema: JsonSchemaObject<ROW>,
|
|
43
41
|
): Promise<void> {
|
|
44
42
|
// no-op
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
async getByIds<ROW extends ObjectWithId>(
|
|
45
|
+
async getByIds<ROW extends ObjectWithId>(_table: string, _ids: string[]): Promise<ROW[]> {
|
|
48
46
|
throw new Error('getByIds is not implemented')
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
async deleteByQuery<ROW extends ObjectWithId>(
|
|
49
|
+
async deleteByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number> {
|
|
52
50
|
throw new Error('deleteByQuery is not implemented')
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
async updateByQuery<ROW extends ObjectWithId>(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
_q: DBQuery<ROW>,
|
|
55
|
+
_patch: DBPatch<ROW>,
|
|
56
|
+
_opt?: CommonDBOptions,
|
|
59
57
|
): Promise<number> {
|
|
60
58
|
throw new Error('updateByQuery is not implemented')
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
async runQuery<ROW extends ObjectWithId>(
|
|
61
|
+
async runQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<RunQueryResult<ROW>> {
|
|
64
62
|
throw new Error('runQuery is not implemented')
|
|
65
63
|
}
|
|
66
64
|
|
|
67
|
-
async runQueryCount<ROW extends ObjectWithId>(
|
|
65
|
+
async runQueryCount<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number> {
|
|
68
66
|
throw new Error('runQueryCount is not implemented')
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
async saveBatch<ROW extends ObjectWithId>(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
_table: string,
|
|
71
|
+
_rows: ROW[],
|
|
72
|
+
_opt?: CommonDBSaveOptions<ROW>,
|
|
75
73
|
): Promise<void> {
|
|
76
74
|
throw new Error('saveBatch is not implemented')
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
streamQuery<ROW extends ObjectWithId>(
|
|
77
|
+
streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW> {
|
|
80
78
|
throw new Error('streamQuery is not implemented')
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
async deleteByIds(
|
|
81
|
+
async deleteByIds(_table: string, _ids: string[], _opt?: CommonDBOptions): Promise<number> {
|
|
84
82
|
throw new Error('deleteByIds is not implemented')
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
async runInTransaction(fn: DBTransactionFn,
|
|
85
|
+
async runInTransaction(fn: DBTransactionFn, _opt?: CommonDBTransactionOptions): Promise<void> {
|
|
88
86
|
const tx = new FakeDBTransaction(this)
|
|
89
87
|
await fn(tx)
|
|
90
88
|
// there's no try/catch and rollback, as there's nothing to rollback
|
|
@@ -17,26 +17,26 @@ import {
|
|
|
17
17
|
import { CommonDB } from '../common.db'
|
|
18
18
|
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model'
|
|
19
19
|
|
|
20
|
-
export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity> {
|
|
20
|
+
export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']> {
|
|
21
21
|
/**
|
|
22
22
|
* Allows to override the id generation function.
|
|
23
23
|
* By default it uses `stringId` from nodejs-lib
|
|
24
24
|
* (which uses lowercase alphanumberic alphabet and the size of 16).
|
|
25
25
|
*/
|
|
26
|
-
createRandomId: () =>
|
|
26
|
+
createRandomId: () => ID
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* createNaturalId hook is called (tried) first.
|
|
30
30
|
* If it doesn't exist - createRandomId is called.
|
|
31
31
|
*/
|
|
32
|
-
createNaturalId: (obj: DBM | BM) =>
|
|
32
|
+
createNaturalId: (obj: DBM | BM) => ID
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* It's a counter-part of `createNaturalId`.
|
|
36
36
|
* Allows to provide a parser function to parse "natural id" into
|
|
37
37
|
* DBM components (e.g accountId and some other property that is part of the id).
|
|
38
38
|
*/
|
|
39
|
-
parseNaturalId: (id:
|
|
39
|
+
parseNaturalId: (id: ID) => Partial<DBM>
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* It is called only on `dao.create` method.
|
|
@@ -118,7 +118,11 @@ export enum CommonDaoLogLevel {
|
|
|
118
118
|
DATA_FULL = 30,
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
export interface CommonDaoCfg<
|
|
121
|
+
export interface CommonDaoCfg<
|
|
122
|
+
BM extends BaseDBEntity,
|
|
123
|
+
DBM extends BaseDBEntity = BM,
|
|
124
|
+
ID = BM['id'],
|
|
125
|
+
> {
|
|
122
126
|
db: CommonDB
|
|
123
127
|
table: string
|
|
124
128
|
|
|
@@ -174,7 +178,7 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
174
178
|
logStarted?: boolean
|
|
175
179
|
|
|
176
180
|
// Hooks are designed with inspiration from got/ky interface
|
|
177
|
-
hooks?: Partial<CommonDaoHooks<BM, DBM>>
|
|
181
|
+
hooks?: Partial<CommonDaoHooks<BM, DBM, ID>>
|
|
178
182
|
|
|
179
183
|
/**
|
|
180
184
|
* Defaults to true.
|
|
@@ -72,8 +72,8 @@ const isCI = !!process.env['CI']
|
|
|
72
72
|
* BM = Backend model (optimized for API access)
|
|
73
73
|
* TM = Transport model (optimized to be sent over the wire)
|
|
74
74
|
*/
|
|
75
|
-
export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
76
|
-
constructor(public cfg: CommonDaoCfg<BM, DBM>) {
|
|
75
|
+
export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM, ID = BM['id']> {
|
|
76
|
+
constructor(public cfg: CommonDaoCfg<BM, DBM, ID>) {
|
|
77
77
|
this.cfg = {
|
|
78
78
|
// Default is to NOT log in AppEngine and in CI,
|
|
79
79
|
// otherwise to log Operations
|
|
@@ -93,11 +93,11 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
93
93
|
anonymize: dbm => dbm,
|
|
94
94
|
onValidationError: err => err,
|
|
95
95
|
...cfg.hooks,
|
|
96
|
-
} satisfies Partial<CommonDaoHooks<BM, DBM>>,
|
|
96
|
+
} satisfies Partial<CommonDaoHooks<BM, DBM, ID>>,
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
if (this.cfg.generateId) {
|
|
100
|
-
this.cfg.hooks!.createRandomId ||= () => stringId()
|
|
100
|
+
this.cfg.hooks!.createRandomId ||= () => stringId() as ID
|
|
101
101
|
} else {
|
|
102
102
|
delete this.cfg.hooks!.createRandomId
|
|
103
103
|
}
|
|
@@ -113,14 +113,14 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
113
113
|
|
|
114
114
|
// GET
|
|
115
115
|
async getById(id: undefined | null, opt?: CommonDaoOptions): Promise<null>
|
|
116
|
-
async getById(id?:
|
|
117
|
-
async getById(id?:
|
|
116
|
+
async getById(id?: ID | null, opt?: CommonDaoOptions): Promise<BM | null>
|
|
117
|
+
async getById(id?: ID | null, opt: CommonDaoOptions = {}): Promise<BM | null> {
|
|
118
118
|
if (!id) return null
|
|
119
119
|
const op = `getById(${id})`
|
|
120
120
|
const table = opt.table || this.cfg.table
|
|
121
121
|
const started = this.logStarted(op, table)
|
|
122
122
|
|
|
123
|
-
let dbm = (await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id]))[0]
|
|
123
|
+
let dbm = (await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id as string]))[0]
|
|
124
124
|
if (dbm && this.cfg.hooks!.afterLoad) {
|
|
125
125
|
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
126
126
|
}
|
|
@@ -130,7 +130,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
130
130
|
return bm || null
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
async getByIdOrEmpty(id:
|
|
133
|
+
async getByIdOrEmpty(id: ID, part: Partial<BM> = {}, opt?: CommonDaoOptions): Promise<BM> {
|
|
134
134
|
const bm = await this.getById(id, opt)
|
|
135
135
|
if (bm) return bm
|
|
136
136
|
|
|
@@ -138,13 +138,13 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
async getByIdAsDBM(id: undefined | null, opt?: CommonDaoOptions): Promise<null>
|
|
141
|
-
async getByIdAsDBM(id?:
|
|
142
|
-
async getByIdAsDBM(id?:
|
|
141
|
+
async getByIdAsDBM(id?: ID | null, opt?: CommonDaoOptions): Promise<DBM | null>
|
|
142
|
+
async getByIdAsDBM(id?: ID | null, opt: CommonDaoOptions = {}): Promise<DBM | null> {
|
|
143
143
|
if (!id) return null
|
|
144
144
|
const op = `getByIdAsDBM(${id})`
|
|
145
145
|
const table = opt.table || this.cfg.table
|
|
146
146
|
const started = this.logStarted(op, table)
|
|
147
|
-
let [dbm] = await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id])
|
|
147
|
+
let [dbm] = await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id as string])
|
|
148
148
|
if (dbm && this.cfg.hooks!.afterLoad) {
|
|
149
149
|
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
150
150
|
}
|
|
@@ -154,12 +154,12 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
154
154
|
return dbm || null
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
async getByIds(ids:
|
|
157
|
+
async getByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<BM[]> {
|
|
158
158
|
if (!ids.length) return []
|
|
159
159
|
const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
160
160
|
const table = opt.table || this.cfg.table
|
|
161
161
|
const started = this.logStarted(op, table)
|
|
162
|
-
let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids)
|
|
162
|
+
let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids as string[])
|
|
163
163
|
if (this.cfg.hooks!.afterLoad && dbms.length) {
|
|
164
164
|
dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
165
165
|
_isTruthy,
|
|
@@ -171,12 +171,12 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
171
171
|
return bms
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
async getByIdsAsDBM(ids:
|
|
174
|
+
async getByIdsAsDBM(ids: ID[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
|
|
175
175
|
if (!ids.length) return []
|
|
176
176
|
const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
177
177
|
const table = opt.table || this.cfg.table
|
|
178
178
|
const started = this.logStarted(op, table)
|
|
179
|
-
let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids)
|
|
179
|
+
let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids as string[])
|
|
180
180
|
if (this.cfg.hooks!.afterLoad && dbms.length) {
|
|
181
181
|
dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
182
182
|
_isTruthy,
|
|
@@ -187,7 +187,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
187
187
|
return dbms
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
async requireById(id:
|
|
190
|
+
async requireById(id: ID, opt: CommonDaoOptions = {}): Promise<BM> {
|
|
191
191
|
const r = await this.getById(id, opt)
|
|
192
192
|
if (!r) {
|
|
193
193
|
this.throwRequiredError(id, opt)
|
|
@@ -195,7 +195,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
195
195
|
return r
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
async requireByIdAsDBM(id:
|
|
198
|
+
async requireByIdAsDBM(id: ID, opt: CommonDaoOptions = {}): Promise<DBM> {
|
|
199
199
|
const r = await this.getByIdAsDBM(id, opt)
|
|
200
200
|
if (!r) {
|
|
201
201
|
this.throwRequiredError(id, opt)
|
|
@@ -203,7 +203,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
203
203
|
return r
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
private throwRequiredError(id:
|
|
206
|
+
private throwRequiredError(id: ID, opt: CommonDaoOptions): never {
|
|
207
207
|
const table = opt.table || this.cfg.table
|
|
208
208
|
throw new AppError(`DB row required, but not found in ${table}`, {
|
|
209
209
|
table,
|
|
@@ -261,8 +261,8 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
261
261
|
/**
|
|
262
262
|
* Pass `table` to override table
|
|
263
263
|
*/
|
|
264
|
-
query(table?: string): RunnableDBQuery<BM, DBM> {
|
|
265
|
-
return new RunnableDBQuery<BM, DBM>(this, table)
|
|
264
|
+
query(table?: string): RunnableDBQuery<BM, DBM, ID> {
|
|
265
|
+
return new RunnableDBQuery<BM, DBM, ID>(this, table)
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
async runQuery(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<BM[]> {
|
|
@@ -295,6 +295,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
async runQueryExtended(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<RunQueryResult<BM>> {
|
|
298
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
298
299
|
q.table = opt.table || q.table
|
|
299
300
|
const op = `runQuery(${q.pretty()})`
|
|
300
301
|
const started = this.logStarted(op, q.table)
|
|
@@ -323,6 +324,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
323
324
|
q: DBQuery<DBM>,
|
|
324
325
|
opt: CommonDaoOptions = {},
|
|
325
326
|
): Promise<RunQueryResult<DBM>> {
|
|
327
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
326
328
|
q.table = opt.table || q.table
|
|
327
329
|
const op = `runQueryAsDBM(${q.pretty()})`
|
|
328
330
|
const started = this.logStarted(op, q.table)
|
|
@@ -340,6 +342,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
340
342
|
}
|
|
341
343
|
|
|
342
344
|
async runQueryCount(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
|
|
345
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
343
346
|
q.table = opt.table || q.table
|
|
344
347
|
const op = `runQueryCount(${q.pretty()})`
|
|
345
348
|
const started = this.logStarted(op, q.table)
|
|
@@ -355,6 +358,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
355
358
|
mapper: AsyncMapper<BM, void>,
|
|
356
359
|
opt: CommonDaoStreamForEachOptions<BM> = {},
|
|
357
360
|
): Promise<void> {
|
|
361
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
358
362
|
q.table = opt.table || q.table
|
|
359
363
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
360
364
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
@@ -404,6 +408,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
404
408
|
mapper: AsyncMapper<DBM, void>,
|
|
405
409
|
opt: CommonDaoStreamForEachOptions<DBM> = {},
|
|
406
410
|
): Promise<void> {
|
|
411
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
407
412
|
q.table = opt.table || q.table
|
|
408
413
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
409
414
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
@@ -452,6 +457,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
452
457
|
* Stream as Readable, to be able to .pipe() it further with support of backpressure.
|
|
453
458
|
*/
|
|
454
459
|
streamQueryAsDBM(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<DBM> = {}): ReadableTyped<DBM> {
|
|
460
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
455
461
|
q.table = opt.table || q.table
|
|
456
462
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
457
463
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
@@ -490,6 +496,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
490
496
|
* You can do `.pipe(transformNoOp)` to make it "valid again".
|
|
491
497
|
*/
|
|
492
498
|
streamQuery(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<BM> = {}): ReadableTyped<BM> {
|
|
499
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
493
500
|
q.table = opt.table || q.table
|
|
494
501
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
495
502
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
@@ -539,21 +546,23 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
539
546
|
)
|
|
540
547
|
}
|
|
541
548
|
|
|
542
|
-
async queryIds(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<
|
|
549
|
+
async queryIds(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<ID[]> {
|
|
550
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
543
551
|
q.table = opt.table || q.table
|
|
544
552
|
const { rows } = await this.cfg.db.runQuery(q.select(['id']), opt)
|
|
545
|
-
return rows.map(r => r.id)
|
|
553
|
+
return rows.map(r => r.id as ID)
|
|
546
554
|
}
|
|
547
555
|
|
|
548
|
-
streamQueryIds(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<
|
|
556
|
+
streamQueryIds(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<ID> = {}): ReadableTyped<ID> {
|
|
557
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
549
558
|
q.table = opt.table || q.table
|
|
550
559
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
551
560
|
|
|
552
561
|
// Experimental: using `.map()`
|
|
553
|
-
const stream: ReadableTyped<
|
|
562
|
+
const stream: ReadableTyped<ID> = this.cfg.db
|
|
554
563
|
.streamQuery<DBM>(q.select(['id']), opt)
|
|
555
564
|
// .on('error', err => stream.emit('error', err))
|
|
556
|
-
.map((r: ObjectWithId) => r.id)
|
|
565
|
+
.map((r: ObjectWithId) => r.id as ID)
|
|
557
566
|
|
|
558
567
|
// const stream: ReadableTyped<string> = this.cfg.db
|
|
559
568
|
// .streamQuery<DBM>(q.select(['id']), opt)
|
|
@@ -569,9 +578,10 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
569
578
|
|
|
570
579
|
async streamQueryIdsForEach(
|
|
571
580
|
q: DBQuery<DBM>,
|
|
572
|
-
mapper: AsyncMapper<
|
|
573
|
-
opt: CommonDaoStreamForEachOptions<
|
|
581
|
+
mapper: AsyncMapper<ID, void>,
|
|
582
|
+
opt: CommonDaoStreamForEachOptions<ID> = {},
|
|
574
583
|
): Promise<void> {
|
|
584
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
575
585
|
q.table = opt.table || q.table
|
|
576
586
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
577
587
|
|
|
@@ -584,7 +594,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
584
594
|
count++
|
|
585
595
|
return r.id
|
|
586
596
|
}),
|
|
587
|
-
transformMap<
|
|
597
|
+
transformMap<ID, void>(mapper, {
|
|
588
598
|
...opt,
|
|
589
599
|
predicate: _passthroughPredicate,
|
|
590
600
|
}),
|
|
@@ -617,7 +627,8 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
617
627
|
}
|
|
618
628
|
|
|
619
629
|
if (this.cfg.generateId) {
|
|
620
|
-
obj.id ||= this.cfg.hooks!.createNaturalId?.(obj as any) ||
|
|
630
|
+
obj.id ||= (this.cfg.hooks!.createNaturalId?.(obj as any) ||
|
|
631
|
+
this.cfg.hooks!.createRandomId!()) as T['id']
|
|
621
632
|
}
|
|
622
633
|
|
|
623
634
|
return obj as T
|
|
@@ -634,7 +645,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
634
645
|
* 3. Saves (as fast as possible since the read) with the Patch applied, but only if the data has changed.
|
|
635
646
|
*/
|
|
636
647
|
async patchById(
|
|
637
|
-
id:
|
|
648
|
+
id: ID,
|
|
638
649
|
patch: Partial<BM>,
|
|
639
650
|
opt: CommonDaoPatchByIdOptions<DBM> = {},
|
|
640
651
|
): Promise<BM> {
|
|
@@ -671,7 +682,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
671
682
|
* Like patchById, but runs all operations within a Transaction.
|
|
672
683
|
*/
|
|
673
684
|
async patchByIdInTransaction(
|
|
674
|
-
id:
|
|
685
|
+
id: ID,
|
|
675
686
|
patch: Partial<BM>,
|
|
676
687
|
opt?: CommonDaoPatchByIdOptions<DBM>,
|
|
677
688
|
): Promise<BM> {
|
|
@@ -706,7 +717,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
706
717
|
}
|
|
707
718
|
Object.assign(bm, patch)
|
|
708
719
|
} else {
|
|
709
|
-
const loaded = await this.getById(bm.id, opt)
|
|
720
|
+
const loaded = await this.getById(bm.id as ID, opt)
|
|
710
721
|
|
|
711
722
|
if (loaded) {
|
|
712
723
|
const loadedWithPatch: BM = {
|
|
@@ -989,19 +1000,19 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
989
1000
|
/**
|
|
990
1001
|
* @returns number of deleted items
|
|
991
1002
|
*/
|
|
992
|
-
async deleteById(id?:
|
|
1003
|
+
async deleteById(id?: ID | null, opt: CommonDaoOptions = {}): Promise<number> {
|
|
993
1004
|
if (!id) return 0
|
|
994
1005
|
return await this.deleteByIds([id], opt)
|
|
995
1006
|
}
|
|
996
1007
|
|
|
997
|
-
async deleteByIds(ids:
|
|
1008
|
+
async deleteByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<number> {
|
|
998
1009
|
if (!ids.length) return 0
|
|
999
1010
|
this.requireWriteAccess()
|
|
1000
1011
|
this.requireObjectMutability(opt)
|
|
1001
1012
|
const op = `deleteByIds(${ids.join(', ')})`
|
|
1002
1013
|
const table = opt.table || this.cfg.table
|
|
1003
1014
|
const started = this.logStarted(op, table)
|
|
1004
|
-
const count = await (opt.tx || this.cfg.db).deleteByIds(table, ids, opt)
|
|
1015
|
+
const count = await (opt.tx || this.cfg.db).deleteByIds(table, ids as string[], opt)
|
|
1005
1016
|
this.logSaveResult(started, op, table)
|
|
1006
1017
|
return count
|
|
1007
1018
|
}
|
|
@@ -1015,6 +1026,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1015
1026
|
q: DBQuery<DBM>,
|
|
1016
1027
|
opt: CommonDaoStreamDeleteOptions<DBM> = {},
|
|
1017
1028
|
): Promise<number> {
|
|
1029
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
1018
1030
|
this.requireWriteAccess()
|
|
1019
1031
|
this.requireObjectMutability(opt)
|
|
1020
1032
|
q.table = opt.table || q.table
|
|
@@ -1055,15 +1067,11 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1055
1067
|
return deleted
|
|
1056
1068
|
}
|
|
1057
1069
|
|
|
1058
|
-
async updateById(id:
|
|
1070
|
+
async updateById(id: ID, patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
|
|
1059
1071
|
return await this.updateByQuery(this.query().filterEq('id', id), patch, opt)
|
|
1060
1072
|
}
|
|
1061
1073
|
|
|
1062
|
-
async updateByIds(
|
|
1063
|
-
ids: string[],
|
|
1064
|
-
patch: DBPatch<DBM>,
|
|
1065
|
-
opt: CommonDaoOptions = {},
|
|
1066
|
-
): Promise<number> {
|
|
1074
|
+
async updateByIds(ids: ID[], patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
|
|
1067
1075
|
if (!ids.length) return 0
|
|
1068
1076
|
return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt)
|
|
1069
1077
|
}
|
|
@@ -1073,6 +1081,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1073
1081
|
patch: DBPatch<DBM>,
|
|
1074
1082
|
opt: CommonDaoOptions = {},
|
|
1075
1083
|
): Promise<number> {
|
|
1084
|
+
this.validateQueryIndexes(q) // throws if query uses `excludeFromIndexes` property
|
|
1076
1085
|
this.requireWriteAccess()
|
|
1077
1086
|
this.requireObjectMutability(opt)
|
|
1078
1087
|
q.table = opt.table || q.table
|
|
@@ -1092,7 +1101,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1092
1101
|
|
|
1093
1102
|
// optimization: no need to run full joi DBM validation, cause BM validation will be run
|
|
1094
1103
|
// const dbm = this.anyToDBM(_dbm, opt)
|
|
1095
|
-
let dbm: DBM = { ..._dbm, ...this.cfg.hooks!.parseNaturalId!(_dbm.id) }
|
|
1104
|
+
let dbm: DBM = { ..._dbm, ...this.cfg.hooks!.parseNaturalId!(_dbm.id as ID) }
|
|
1096
1105
|
|
|
1097
1106
|
if (opt.anonymize) {
|
|
1098
1107
|
dbm = this.cfg.hooks!.anonymize!(dbm)
|
|
@@ -1138,7 +1147,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1138
1147
|
// this shouldn't be happening on load! but should on save!
|
|
1139
1148
|
// this.assignIdCreatedUpdated(dbm, opt)
|
|
1140
1149
|
|
|
1141
|
-
dbm = { ...dbm, ...this.cfg.hooks!.parseNaturalId!(dbm.id) }
|
|
1150
|
+
dbm = { ...dbm, ...this.cfg.hooks!.parseNaturalId!(dbm.id as ID) }
|
|
1142
1151
|
|
|
1143
1152
|
// todo: is this the right place?
|
|
1144
1153
|
// todo: is anyToDBM even needed?
|
|
@@ -1259,6 +1268,24 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1259
1268
|
return r!
|
|
1260
1269
|
}
|
|
1261
1270
|
|
|
1271
|
+
/**
|
|
1272
|
+
* Throws if query uses a property that is in `excludeFromIndexes` list.
|
|
1273
|
+
*/
|
|
1274
|
+
private validateQueryIndexes(q: DBQuery<DBM>): void {
|
|
1275
|
+
const { excludeFromIndexes } = this.cfg
|
|
1276
|
+
if (!excludeFromIndexes) return
|
|
1277
|
+
|
|
1278
|
+
for (const f of q._filters) {
|
|
1279
|
+
_assert(
|
|
1280
|
+
!excludeFromIndexes.includes(f.name),
|
|
1281
|
+
`cannot query on non-indexed property: ${this.cfg.table}.${f.name as string}`,
|
|
1282
|
+
{
|
|
1283
|
+
query: q.pretty(),
|
|
1284
|
+
},
|
|
1285
|
+
)
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1262
1289
|
protected logResult(started: number, op: string, res: any, table: string): void {
|
|
1263
1290
|
if (!this.cfg.logLevel) return
|
|
1264
1291
|
|
|
@@ -1345,17 +1372,17 @@ export class CommonDaoTransaction {
|
|
|
1345
1372
|
}
|
|
1346
1373
|
}
|
|
1347
1374
|
|
|
1348
|
-
async getById<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
|
|
1349
|
-
dao: CommonDao<BM, DBM>,
|
|
1350
|
-
id?:
|
|
1375
|
+
async getById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
|
|
1376
|
+
dao: CommonDao<BM, DBM, ID>,
|
|
1377
|
+
id?: ID | null,
|
|
1351
1378
|
opt?: CommonDaoOptions,
|
|
1352
1379
|
): Promise<BM | null> {
|
|
1353
1380
|
return await dao.getById(id, { ...opt, tx: this.tx })
|
|
1354
1381
|
}
|
|
1355
1382
|
|
|
1356
|
-
async getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
|
|
1357
|
-
dao: CommonDao<BM, DBM>,
|
|
1358
|
-
ids:
|
|
1383
|
+
async getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
|
|
1384
|
+
dao: CommonDao<BM, DBM, ID>,
|
|
1385
|
+
ids: ID[],
|
|
1359
1386
|
opt?: CommonDaoOptions,
|
|
1360
1387
|
): Promise<BM[]> {
|
|
1361
1388
|
return await dao.getByIds(ids, { ...opt, tx: this.tx })
|
|
@@ -1398,8 +1425,8 @@ export class CommonDaoTransaction {
|
|
|
1398
1425
|
*
|
|
1399
1426
|
* So, this method is a rather simple convenience "Object.assign and then save".
|
|
1400
1427
|
*/
|
|
1401
|
-
async patch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
|
|
1402
|
-
dao: CommonDao<BM, DBM>,
|
|
1428
|
+
async patch<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
|
|
1429
|
+
dao: CommonDao<BM, DBM, ID>,
|
|
1403
1430
|
bm: BM,
|
|
1404
1431
|
patch: Partial<BM>,
|
|
1405
1432
|
opt?: CommonDaoSaveOptions<BM, DBM>,
|
|
@@ -1409,16 +1436,20 @@ export class CommonDaoTransaction {
|
|
|
1409
1436
|
return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx })
|
|
1410
1437
|
}
|
|
1411
1438
|
|
|
1412
|
-
async deleteById(
|
|
1413
|
-
dao: CommonDao<
|
|
1414
|
-
id?:
|
|
1439
|
+
async deleteById<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
|
|
1440
|
+
dao: CommonDao<BM, DBM, ID>,
|
|
1441
|
+
id?: ID | null,
|
|
1415
1442
|
opt?: CommonDaoOptions,
|
|
1416
1443
|
): Promise<number> {
|
|
1417
1444
|
if (!id) return 0
|
|
1418
1445
|
return await this.deleteByIds(dao, [id], opt)
|
|
1419
1446
|
}
|
|
1420
1447
|
|
|
1421
|
-
async deleteByIds
|
|
1448
|
+
async deleteByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity, ID = BM['id']>(
|
|
1449
|
+
dao: CommonDao<BM, DBM, ID>,
|
|
1450
|
+
ids: ID[],
|
|
1451
|
+
opt?: CommonDaoOptions,
|
|
1452
|
+
): Promise<number> {
|
|
1422
1453
|
return await dao.deleteByIds(ids, { ...opt, tx: this.tx })
|
|
1423
1454
|
}
|
|
1424
1455
|
}
|
package/src/query/dbQuery.ts
CHANGED
|
@@ -193,7 +193,7 @@ export class DBQuery<ROW extends ObjectWithId> {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
prettyConditions(): string[] {
|
|
196
|
-
const tokens = []
|
|
196
|
+
const tokens: string[] = []
|
|
197
197
|
|
|
198
198
|
// if (this.name) {
|
|
199
199
|
// tokens.push(`"${this.name}"`)
|
|
@@ -240,12 +240,13 @@ export class DBQuery<ROW extends ObjectWithId> {
|
|
|
240
240
|
export class RunnableDBQuery<
|
|
241
241
|
BM extends BaseDBEntity,
|
|
242
242
|
DBM extends BaseDBEntity = BM,
|
|
243
|
+
ID = BM['id'],
|
|
243
244
|
> extends DBQuery<DBM> {
|
|
244
245
|
/**
|
|
245
246
|
* Pass `table` to override table.
|
|
246
247
|
*/
|
|
247
248
|
constructor(
|
|
248
|
-
public dao: CommonDao<BM, DBM>,
|
|
249
|
+
public dao: CommonDao<BM, DBM, ID>,
|
|
249
250
|
table?: string,
|
|
250
251
|
) {
|
|
251
252
|
super(table || dao.cfg.table)
|
|
@@ -301,17 +302,17 @@ export class RunnableDBQuery<
|
|
|
301
302
|
return this.dao.streamQueryAsDBM(this, opt)
|
|
302
303
|
}
|
|
303
304
|
|
|
304
|
-
async queryIds(opt?: CommonDaoOptions): Promise<
|
|
305
|
+
async queryIds(opt?: CommonDaoOptions): Promise<ID[]> {
|
|
305
306
|
return await this.dao.queryIds(this, opt)
|
|
306
307
|
}
|
|
307
308
|
|
|
308
|
-
streamQueryIds(opt?: CommonDaoStreamOptions<
|
|
309
|
+
streamQueryIds(opt?: CommonDaoStreamOptions<ID>): ReadableTyped<ID> {
|
|
309
310
|
return this.dao.streamQueryIds(this, opt)
|
|
310
311
|
}
|
|
311
312
|
|
|
312
313
|
async streamQueryIdsForEach(
|
|
313
|
-
mapper: AsyncMapper<
|
|
314
|
-
opt?: CommonDaoStreamForEachOptions<
|
|
314
|
+
mapper: AsyncMapper<ID, void>,
|
|
315
|
+
opt?: CommonDaoStreamForEachOptions<ID>,
|
|
315
316
|
): Promise<void> {
|
|
316
317
|
await this.dao.streamQueryIdsForEach(this, mapper, opt)
|
|
317
318
|
}
|
package/src/testing/daoTest.ts
CHANGED
package/src/testing/dbTest.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _filterObject, _pick, _sortBy, pMap } from '@naturalcycles/js-lib'
|
|
1
|
+
import { _deepFreeze, _filterObject, _pick, _sortBy, pMap } from '@naturalcycles/js-lib'
|
|
2
2
|
import { CommonDB, CommonDBType } from '../common.db'
|
|
3
3
|
import { DBIncrement, DBPatch } from '../db.model'
|
|
4
4
|
import { DBQuery } from '../query/dbQuery'
|
|
@@ -9,9 +9,6 @@ import {
|
|
|
9
9
|
testItemBMJsonSchema,
|
|
10
10
|
TestItemDBM,
|
|
11
11
|
} from './test.model'
|
|
12
|
-
import { deepFreeze } from './test.util'
|
|
13
|
-
|
|
14
|
-
/* eslint-disable jest/no-export */
|
|
15
12
|
|
|
16
13
|
/**
|
|
17
14
|
* All options default to `false`.
|
|
@@ -31,7 +28,7 @@ export interface CommonDBImplementationQuirks {
|
|
|
31
28
|
export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuirks = {}): void {
|
|
32
29
|
const { support } = db
|
|
33
30
|
const items = createTestItemsDBM(3)
|
|
34
|
-
|
|
31
|
+
_deepFreeze(items)
|
|
35
32
|
const item1 = items[0]!
|
|
36
33
|
|
|
37
34
|
const queryAll = (): DBQuery<TestItemDBM> => DBQuery.create<TestItemDBM>(TEST_TABLE)
|
|
@@ -88,7 +85,7 @@ export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuir
|
|
|
88
85
|
...createTestItemDBM(3),
|
|
89
86
|
k2: null,
|
|
90
87
|
}
|
|
91
|
-
|
|
88
|
+
_deepFreeze(item3)
|
|
92
89
|
await db.saveBatch(TEST_TABLE, [item3])
|
|
93
90
|
const item3Loaded = (await db.getByIds<TestItemDBM>(TEST_TABLE, [item3.id]))[0]!
|
|
94
91
|
expectMatch([item3], [item3Loaded], quirks)
|
|
@@ -102,7 +99,7 @@ export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuir
|
|
|
102
99
|
...createTestItemDBM(3),
|
|
103
100
|
k2: undefined,
|
|
104
101
|
}
|
|
105
|
-
|
|
102
|
+
_deepFreeze(item3)
|
|
106
103
|
const expected = { ...item3 }
|
|
107
104
|
delete expected.k2
|
|
108
105
|
|
|
@@ -2,8 +2,6 @@ import { _range, _sortBy } from '@naturalcycles/js-lib'
|
|
|
2
2
|
import { CommonKeyValueDB, KeyValueDBTuple } from '../kv/commonKeyValueDB'
|
|
3
3
|
import { TEST_TABLE } from './test.model'
|
|
4
4
|
|
|
5
|
-
/* eslint-disable jest/no-export */
|
|
6
|
-
|
|
7
5
|
const testIds = _range(1, 4).map(n => `id${n}`)
|
|
8
6
|
|
|
9
7
|
const testEntries: KeyValueDBTuple[] = testIds.map(id => [id, Buffer.from(`${id}value`)])
|
|
@@ -2,8 +2,6 @@ import { _range, _sortBy } from '@naturalcycles/js-lib'
|
|
|
2
2
|
import { CommonKeyValueDao } from '../kv/commonKeyValueDao'
|
|
3
3
|
import { KeyValueDBTuple } from '../kv/commonKeyValueDB'
|
|
4
4
|
|
|
5
|
-
/* eslint-disable jest/no-export */
|
|
6
|
-
|
|
7
5
|
const testIds = _range(1, 4).map(n => `id${n}`)
|
|
8
6
|
const testEntries: KeyValueDBTuple[] = testIds.map(id => [id, Buffer.from(`${id}value`)])
|
|
9
7
|
|
|
@@ -120,11 +120,7 @@ export class FakeDBTransaction implements DBTransaction {
|
|
|
120
120
|
): Promise<void> {
|
|
121
121
|
await this.db.saveBatch(table, rows, opt)
|
|
122
122
|
}
|
|
123
|
-
async deleteByIds(
|
|
124
|
-
table: string,
|
|
125
|
-
ids: string[],
|
|
126
|
-
opt?: CommonDBOptions | undefined,
|
|
127
|
-
): Promise<number> {
|
|
123
|
+
async deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number> {
|
|
128
124
|
return await this.db.deleteByIds(table, ids, opt)
|
|
129
125
|
}
|
|
130
126
|
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// copy-pasted from test-lib to be able to not include devDependecies there
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.deepFreeze = deepFreeze;
|
|
5
|
-
/**
|
|
6
|
-
* Does Object.freeze recursively for given object.
|
|
7
|
-
*
|
|
8
|
-
* Based on: https://github.com/substack/deep-freeze/blob/master/index.js
|
|
9
|
-
*/
|
|
10
|
-
function deepFreeze(o) {
|
|
11
|
-
Object.freeze(o);
|
|
12
|
-
Object.getOwnPropertyNames(o).forEach(prop => {
|
|
13
|
-
if (
|
|
14
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
15
|
-
o.hasOwnProperty(prop) &&
|
|
16
|
-
o[prop] !== null &&
|
|
17
|
-
(typeof o[prop] === 'object' || typeof o[prop] === 'function') &&
|
|
18
|
-
!Object.isFrozen(o[prop])) {
|
|
19
|
-
deepFreeze(o[prop]);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
return o;
|
|
23
|
-
}
|
package/src/testing/test.util.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// copy-pasted from test-lib to be able to not include devDependecies there
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Does Object.freeze recursively for given object.
|
|
5
|
-
*
|
|
6
|
-
* Based on: https://github.com/substack/deep-freeze/blob/master/index.js
|
|
7
|
-
*/
|
|
8
|
-
export function deepFreeze(o: any): void {
|
|
9
|
-
Object.freeze(o)
|
|
10
|
-
|
|
11
|
-
Object.getOwnPropertyNames(o).forEach(prop => {
|
|
12
|
-
if (
|
|
13
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
14
|
-
o.hasOwnProperty(prop) &&
|
|
15
|
-
o[prop] !== null &&
|
|
16
|
-
(typeof o[prop] === 'object' || typeof o[prop] === 'function') &&
|
|
17
|
-
!Object.isFrozen(o[prop])
|
|
18
|
-
) {
|
|
19
|
-
deepFreeze(o[prop])
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
return o
|
|
24
|
-
}
|