@naturalcycles/db-lib 8.53.0 → 8.54.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,4 +1,4 @@
|
|
|
1
|
-
import { AnyObject, AsyncMapper, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, Promisable, Saved, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
|
|
1
|
+
import { AnyObject, AsyncMapper, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, Promisable, Saved, UnixTimestampMillisNumber, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
|
|
2
2
|
import { AjvSchema, ObjectSchemaTyped, ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
3
|
import { DBDeleteByIdsOperation, DBModelType, DBOperation, DBPatch, DBSaveBatchOperation, RunQueryResult } from '../db.model';
|
|
4
4
|
import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
|
|
@@ -84,7 +84,7 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
|
|
|
84
84
|
assignIdCreatedUpdated(obj: BM, opt?: CommonDaoOptions): Saved<BM>;
|
|
85
85
|
assignIdCreatedUpdated(obj: Unsaved<BM>, opt?: CommonDaoOptions): Saved<BM>;
|
|
86
86
|
tx: {
|
|
87
|
-
save: (bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<DBM>) => Promise<DBSaveBatchOperation>;
|
|
87
|
+
save: (bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<DBM>) => Promise<DBSaveBatchOperation | undefined>;
|
|
88
88
|
saveBatch: (bms: Unsaved<BM>[], opt?: CommonDaoSaveOptions<DBM>) => Promise<DBSaveBatchOperation | undefined>;
|
|
89
89
|
deleteByIds: (ids: ID[], opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation | undefined>;
|
|
90
90
|
deleteById: (id: ID | null | undefined, opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation | undefined>;
|
|
@@ -155,6 +155,6 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
|
|
|
155
155
|
runInTransaction(ops: Promisable<DBOperation | undefined>[]): Promise<void>;
|
|
156
156
|
protected logResult(started: number, op: string, res: any, table: string): void;
|
|
157
157
|
protected logSaveResult(started: number, op: string, table: string): void;
|
|
158
|
-
protected logStarted(op: string, table: string, force?: boolean):
|
|
159
|
-
protected logSaveStarted(op: string, items: any, table: string):
|
|
158
|
+
protected logStarted(op: string, table: string, force?: boolean): UnixTimestampMillisNumber;
|
|
159
|
+
protected logSaveStarted(op: string, items: any, table: string): UnixTimestampMillisNumber;
|
|
160
160
|
}
|
|
@@ -22,7 +22,10 @@ class CommonDao {
|
|
|
22
22
|
this.cfg = cfg;
|
|
23
23
|
this.tx = {
|
|
24
24
|
save: async (bm, opt = {}) => {
|
|
25
|
+
// .save actually returns DBM (not BM) when it detects `opt.tx === true`
|
|
25
26
|
const row = (await this.save(bm, { ...opt, tx: true }));
|
|
27
|
+
if (row === null)
|
|
28
|
+
return;
|
|
26
29
|
return {
|
|
27
30
|
type: 'saveBatch',
|
|
28
31
|
table: this.cfg.table,
|
|
@@ -34,9 +37,9 @@ class CommonDao {
|
|
|
34
37
|
};
|
|
35
38
|
},
|
|
36
39
|
saveBatch: async (bms, opt = {}) => {
|
|
37
|
-
if (!bms.length)
|
|
38
|
-
return;
|
|
39
40
|
const rows = (await this.saveBatch(bms, { ...opt, tx: true }));
|
|
41
|
+
if (!rows.length)
|
|
42
|
+
return;
|
|
40
43
|
return {
|
|
41
44
|
type: 'saveBatch',
|
|
42
45
|
table: this.cfg.table,
|
|
@@ -113,7 +116,10 @@ class CommonDao {
|
|
|
113
116
|
const op = `getById(${id})`;
|
|
114
117
|
const table = opt.table || this.cfg.table;
|
|
115
118
|
const started = this.logStarted(op, table);
|
|
116
|
-
|
|
119
|
+
let dbm = (await this.cfg.db.getByIds(table, [id]))[0];
|
|
120
|
+
if (dbm && !opt.raw && this.cfg.hooks.afterLoad) {
|
|
121
|
+
dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
|
|
122
|
+
}
|
|
117
123
|
const bm = opt.raw ? dbm : await this.dbmToBM(dbm, opt);
|
|
118
124
|
this.logResult(started, op, bm, table);
|
|
119
125
|
return bm || null;
|
|
@@ -138,6 +144,9 @@ class CommonDao {
|
|
|
138
144
|
const table = opt.table || this.cfg.table;
|
|
139
145
|
const started = this.logStarted(op, table);
|
|
140
146
|
let [dbm] = await this.cfg.db.getByIds(table, [id]);
|
|
147
|
+
if (dbm && !opt.raw && this.cfg.hooks.afterLoad) {
|
|
148
|
+
dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
|
|
149
|
+
}
|
|
141
150
|
if (!opt.raw) {
|
|
142
151
|
dbm = this.anyToDBM(dbm, opt);
|
|
143
152
|
}
|
|
@@ -150,7 +159,10 @@ class CommonDao {
|
|
|
150
159
|
const op = `getByIdAsTM(${id})`;
|
|
151
160
|
const table = opt.table || this.cfg.table;
|
|
152
161
|
const started = this.logStarted(op, table);
|
|
153
|
-
|
|
162
|
+
let [dbm] = await this.cfg.db.getByIds(table, [id]);
|
|
163
|
+
if (dbm && !opt.raw && this.cfg.hooks.afterLoad) {
|
|
164
|
+
dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
|
|
165
|
+
}
|
|
154
166
|
if (opt.raw) {
|
|
155
167
|
this.logResult(started, op, dbm, table);
|
|
156
168
|
return dbm || null;
|
|
@@ -161,19 +173,29 @@ class CommonDao {
|
|
|
161
173
|
return tm || null;
|
|
162
174
|
}
|
|
163
175
|
async getByIds(ids, opt = {}) {
|
|
176
|
+
if (!ids.length)
|
|
177
|
+
return [];
|
|
164
178
|
const op = `getByIds ${ids.length} id(s) (${(0, js_lib_1._truncate)(ids.slice(0, 10).join(', '), 50)})`;
|
|
165
179
|
const table = opt.table || this.cfg.table;
|
|
166
180
|
const started = this.logStarted(op, table);
|
|
167
|
-
|
|
181
|
+
let dbms = await this.cfg.db.getByIds(table, ids);
|
|
182
|
+
if (!opt.raw && this.cfg.hooks.afterLoad && dbms.length) {
|
|
183
|
+
dbms = (await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
|
|
184
|
+
}
|
|
168
185
|
const bms = opt.raw ? dbms : await this.dbmsToBM(dbms, opt);
|
|
169
186
|
this.logResult(started, op, bms, table);
|
|
170
187
|
return bms;
|
|
171
188
|
}
|
|
172
189
|
async getByIdsAsDBM(ids, opt = {}) {
|
|
190
|
+
if (!ids.length)
|
|
191
|
+
return [];
|
|
173
192
|
const op = `getByIdsAsDBM ${ids.length} id(s) (${(0, js_lib_1._truncate)(ids.slice(0, 10).join(', '), 50)})`;
|
|
174
193
|
const table = opt.table || this.cfg.table;
|
|
175
194
|
const started = this.logStarted(op, table);
|
|
176
|
-
|
|
195
|
+
let dbms = await this.cfg.db.getByIds(table, ids);
|
|
196
|
+
if (!opt.raw && this.cfg.hooks.afterLoad && dbms.length) {
|
|
197
|
+
dbms = (await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
|
|
198
|
+
}
|
|
177
199
|
this.logResult(started, op, dbms, table);
|
|
178
200
|
return dbms;
|
|
179
201
|
}
|
|
@@ -272,8 +294,11 @@ class CommonDao {
|
|
|
272
294
|
q.table = opt.table || q.table;
|
|
273
295
|
const op = `runQuery(${q.pretty()})`;
|
|
274
296
|
const started = this.logStarted(op, q.table);
|
|
275
|
-
|
|
297
|
+
let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
276
298
|
const partialQuery = !!q._selectedFieldNames;
|
|
299
|
+
if (!opt.raw && this.cfg.hooks.afterLoad && rows.length) {
|
|
300
|
+
rows = (await (0, js_lib_1.pMap)(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
|
|
301
|
+
}
|
|
277
302
|
const bms = partialQuery || opt.raw ? rows : await this.dbmsToBM(rows, opt);
|
|
278
303
|
this.logResult(started, op, bms, q.table);
|
|
279
304
|
return {
|
|
@@ -289,7 +314,10 @@ class CommonDao {
|
|
|
289
314
|
q.table = opt.table || q.table;
|
|
290
315
|
const op = `runQueryAsDBM(${q.pretty()})`;
|
|
291
316
|
const started = this.logStarted(op, q.table);
|
|
292
|
-
|
|
317
|
+
let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
318
|
+
if (!opt.raw && this.cfg.hooks.afterLoad && rows.length) {
|
|
319
|
+
rows = (await (0, js_lib_1.pMap)(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
|
|
320
|
+
}
|
|
293
321
|
const partialQuery = !!q._selectedFieldNames;
|
|
294
322
|
const dbms = partialQuery || opt.raw ? rows : this.anyToDBMs(rows, opt);
|
|
295
323
|
this.logResult(started, op, dbms, q.table);
|
|
@@ -303,7 +331,10 @@ class CommonDao {
|
|
|
303
331
|
q.table = opt.table || q.table;
|
|
304
332
|
const op = `runQueryAsTM(${q.pretty()})`;
|
|
305
333
|
const started = this.logStarted(op, q.table);
|
|
306
|
-
|
|
334
|
+
let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
335
|
+
if (!opt.raw && this.cfg.hooks.afterLoad && rows.length) {
|
|
336
|
+
rows = (await (0, js_lib_1.pMap)(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
|
|
337
|
+
}
|
|
307
338
|
const partialQuery = !!q._selectedFieldNames;
|
|
308
339
|
const tms = partialQuery || opt.raw ? rows : this.bmsToTM(await this.dbmsToBM(rows, opt), opt);
|
|
309
340
|
this.logResult(started, op, tms, q.table);
|
|
@@ -333,11 +364,16 @@ class CommonDao {
|
|
|
333
364
|
let count = 0;
|
|
334
365
|
await (0, nodejs_lib_1._pipeline)([
|
|
335
366
|
this.cfg.db.streamQuery(q, opt),
|
|
336
|
-
// optimization: 1 validation is enough
|
|
337
|
-
// transformMap<any, DBM>(dbm => (partialQuery || opt.raw ? dbm : this.anyToDBM(dbm, opt)), opt),
|
|
338
367
|
(0, nodejs_lib_1.transformMap)(async (dbm) => {
|
|
339
368
|
count++;
|
|
340
|
-
|
|
369
|
+
if (partialQuery || opt.raw)
|
|
370
|
+
return dbm;
|
|
371
|
+
if (this.cfg.hooks.afterLoad) {
|
|
372
|
+
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
373
|
+
if (dbm === null)
|
|
374
|
+
return js_lib_1.SKIP;
|
|
375
|
+
}
|
|
376
|
+
return await this.dbmToBM(dbm, opt);
|
|
341
377
|
}, {
|
|
342
378
|
errorMode: opt.errorMode,
|
|
343
379
|
}),
|
|
@@ -367,9 +403,16 @@ class CommonDao {
|
|
|
367
403
|
let count = 0;
|
|
368
404
|
await (0, nodejs_lib_1._pipeline)([
|
|
369
405
|
this.cfg.db.streamQuery(q, opt),
|
|
370
|
-
(0, nodejs_lib_1.
|
|
406
|
+
(0, nodejs_lib_1.transformMap)(async (dbm) => {
|
|
371
407
|
count++;
|
|
372
|
-
|
|
408
|
+
if (partialQuery || opt.raw)
|
|
409
|
+
return dbm;
|
|
410
|
+
if (this.cfg.hooks.afterLoad) {
|
|
411
|
+
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
412
|
+
if (dbm === null)
|
|
413
|
+
return js_lib_1.SKIP;
|
|
414
|
+
}
|
|
415
|
+
return this.anyToDBM(dbm, opt);
|
|
373
416
|
}, {
|
|
374
417
|
errorMode: opt.errorMode,
|
|
375
418
|
}),
|
|
@@ -402,8 +445,15 @@ class CommonDao {
|
|
|
402
445
|
return stream;
|
|
403
446
|
return stream
|
|
404
447
|
.on('error', err => stream.emit('error', err))
|
|
405
|
-
.pipe((0, nodejs_lib_1.
|
|
406
|
-
|
|
448
|
+
.pipe((0, nodejs_lib_1.transformMap)(async (dbm) => {
|
|
449
|
+
if (this.cfg.hooks.afterLoad) {
|
|
450
|
+
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
451
|
+
if (dbm === null)
|
|
452
|
+
return js_lib_1.SKIP;
|
|
453
|
+
}
|
|
454
|
+
return this.anyToDBM(dbm, opt);
|
|
455
|
+
}, {
|
|
456
|
+
errorMode: opt.errorMode,
|
|
407
457
|
}));
|
|
408
458
|
}
|
|
409
459
|
/**
|
|
@@ -429,8 +479,15 @@ class CommonDao {
|
|
|
429
479
|
// .pipe(transformMap<any, DBM>(dbm => this.anyToDBM(dbm, opt), safeOpt))
|
|
430
480
|
// .pipe(transformMap<DBM, Saved<BM>>(dbm => this.dbmToBM(dbm, opt), safeOpt))
|
|
431
481
|
.on('error', err => stream.emit('error', err))
|
|
432
|
-
.pipe((0, nodejs_lib_1.transformMap)(async (dbm) =>
|
|
433
|
-
|
|
482
|
+
.pipe((0, nodejs_lib_1.transformMap)(async (dbm) => {
|
|
483
|
+
if (this.cfg.hooks.afterLoad) {
|
|
484
|
+
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
485
|
+
if (dbm === null)
|
|
486
|
+
return js_lib_1.SKIP;
|
|
487
|
+
}
|
|
488
|
+
return await this.dbmToBM(dbm, opt);
|
|
489
|
+
}, {
|
|
490
|
+
errorMode: opt.errorMode,
|
|
434
491
|
}))
|
|
435
492
|
// this can make the stream async-iteration-friendly
|
|
436
493
|
// but not applying it now for perf reasons
|
|
@@ -461,8 +518,10 @@ class CommonDao {
|
|
|
461
518
|
let count = 0;
|
|
462
519
|
await (0, nodejs_lib_1._pipeline)([
|
|
463
520
|
this.cfg.db.streamQuery(q.select(['id']), opt),
|
|
464
|
-
(0, nodejs_lib_1.transformMapSimple)(objectWithId =>
|
|
465
|
-
|
|
521
|
+
(0, nodejs_lib_1.transformMapSimple)(objectWithId => {
|
|
522
|
+
count++;
|
|
523
|
+
return objectWithId.id;
|
|
524
|
+
}),
|
|
466
525
|
(0, nodejs_lib_1.transformMap)(mapper, {
|
|
467
526
|
...opt,
|
|
468
527
|
predicate: js_lib_1._passthroughPredicate,
|
|
@@ -502,8 +561,14 @@ class CommonDao {
|
|
|
502
561
|
this.requireWriteAccess();
|
|
503
562
|
const idWasGenerated = !bm.id && this.cfg.createId;
|
|
504
563
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
505
|
-
|
|
564
|
+
let dbm = await this.bmToDBM(bm, opt);
|
|
565
|
+
if (this.cfg.hooks.beforeSave) {
|
|
566
|
+
dbm = (await this.cfg.hooks.beforeSave(dbm));
|
|
567
|
+
if (dbm === null && !opt.tx)
|
|
568
|
+
return bm;
|
|
569
|
+
}
|
|
506
570
|
if (opt.tx) {
|
|
571
|
+
// May return `null`, in which case it'll be skipped
|
|
507
572
|
return dbm;
|
|
508
573
|
}
|
|
509
574
|
const table = opt.table || this.cfg.table;
|
|
@@ -569,6 +634,11 @@ class CommonDao {
|
|
|
569
634
|
const started = this.logSaveStarted(op, row, table);
|
|
570
635
|
const { excludeFromIndexes } = this.cfg;
|
|
571
636
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
637
|
+
if (this.cfg.hooks.beforeSave) {
|
|
638
|
+
row = (await this.cfg.hooks.beforeSave(row));
|
|
639
|
+
if (row === null)
|
|
640
|
+
return dbm;
|
|
641
|
+
}
|
|
572
642
|
await this.cfg.db.saveBatch(table, [row], {
|
|
573
643
|
excludeFromIndexes,
|
|
574
644
|
assignGeneratedIds,
|
|
@@ -581,10 +651,15 @@ class CommonDao {
|
|
|
581
651
|
return row;
|
|
582
652
|
}
|
|
583
653
|
async saveBatch(bms, opt = {}) {
|
|
654
|
+
if (!bms.length)
|
|
655
|
+
return [];
|
|
584
656
|
this.requireWriteAccess();
|
|
585
657
|
const table = opt.table || this.cfg.table;
|
|
586
658
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt));
|
|
587
|
-
|
|
659
|
+
let dbms = await this.bmsToDBM(bms, opt);
|
|
660
|
+
if (this.cfg.hooks.beforeSave && dbms.length) {
|
|
661
|
+
dbms = (await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.cfg.hooks.beforeSave(dbm))).filter(js_lib_1._isTruthy);
|
|
662
|
+
}
|
|
588
663
|
if (opt.tx) {
|
|
589
664
|
return dbms;
|
|
590
665
|
}
|
|
@@ -612,6 +687,8 @@ class CommonDao {
|
|
|
612
687
|
return bms;
|
|
613
688
|
}
|
|
614
689
|
async saveBatchAsDBM(dbms, opt = {}) {
|
|
690
|
+
if (!dbms.length)
|
|
691
|
+
return [];
|
|
615
692
|
this.requireWriteAccess();
|
|
616
693
|
const table = opt.table || this.cfg.table;
|
|
617
694
|
let rows = dbms;
|
|
@@ -631,6 +708,9 @@ class CommonDao {
|
|
|
631
708
|
const started = this.logSaveStarted(op, rows, table);
|
|
632
709
|
const { excludeFromIndexes } = this.cfg;
|
|
633
710
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
711
|
+
if (this.cfg.hooks.beforeSave && rows.length) {
|
|
712
|
+
rows = (await (0, js_lib_1.pMap)(rows, async (row) => await this.cfg.hooks.beforeSave(row))).filter(js_lib_1._isTruthy);
|
|
713
|
+
}
|
|
634
714
|
await this.cfg.db.saveBatch(table, rows, {
|
|
635
715
|
excludeFromIndexes,
|
|
636
716
|
assignGeneratedIds,
|
|
@@ -655,6 +735,8 @@ class CommonDao {
|
|
|
655
735
|
return count;
|
|
656
736
|
}
|
|
657
737
|
async deleteByIds(ids, opt = {}) {
|
|
738
|
+
if (!ids.length)
|
|
739
|
+
return 0;
|
|
658
740
|
this.requireWriteAccess();
|
|
659
741
|
this.requireObjectMutability(opt);
|
|
660
742
|
const op = `deleteByIds(${ids.join(', ')})`;
|
|
@@ -709,6 +791,8 @@ class CommonDao {
|
|
|
709
791
|
return await this.updateByQuery(this.query().filterEq('id', id), patch, opt);
|
|
710
792
|
}
|
|
711
793
|
async updateByIds(ids, patch, opt = {}) {
|
|
794
|
+
if (!ids.length)
|
|
795
|
+
return 0;
|
|
712
796
|
return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt);
|
|
713
797
|
}
|
|
714
798
|
async updateByQuery(q, patch, opt = {}) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommonLogger, ErrorMode, ObjectWithId, Saved, ZodError, ZodSchema } from '@naturalcycles/js-lib';
|
|
1
|
+
import { CommonLogger, ErrorMode, ObjectWithId, Promisable, Saved, ZodError, ZodSchema } from '@naturalcycles/js-lib';
|
|
2
2
|
import { AjvSchema, AjvValidationError, JoiValidationError, ObjectSchemaTyped, TransformLogProgressOptions, TransformMapOptions } from '@naturalcycles/nodejs-lib';
|
|
3
3
|
import { CommonDB } from '../common.db';
|
|
4
4
|
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model';
|
|
@@ -30,7 +30,7 @@ export interface CommonDaoHooks<BM extends Partial<ObjectWithId<ID>>, DBM extend
|
|
|
30
30
|
beforeCreate: (bm: Partial<BM>) => Partial<BM>;
|
|
31
31
|
/**
|
|
32
32
|
* Called when loading things "as DBM" and validation is not skipped.
|
|
33
|
-
* When loading things
|
|
33
|
+
* When loading things as BM/TM - other hooks get involved instead:
|
|
34
34
|
* - beforeDBMToBM
|
|
35
35
|
* - beforeBMToTM
|
|
36
36
|
*
|
|
@@ -41,6 +41,33 @@ export interface CommonDaoHooks<BM extends Partial<ObjectWithId<ID>>, DBM extend
|
|
|
41
41
|
beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>;
|
|
42
42
|
beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>;
|
|
43
43
|
beforeBMToTM: (bm: BM) => Partial<TM>;
|
|
44
|
+
/**
|
|
45
|
+
* Allows to access the DBM just after it has been loaded from the DB.
|
|
46
|
+
*
|
|
47
|
+
* Normally does nothing.
|
|
48
|
+
*
|
|
49
|
+
* You can change the DBM as you want here: ok to mutate or not, but you need to return the DBM
|
|
50
|
+
* to pass it further.
|
|
51
|
+
*
|
|
52
|
+
* You can return `null` to make it look "not found".
|
|
53
|
+
*
|
|
54
|
+
* You can do validations as needed here and throw errors, they will be propagated.
|
|
55
|
+
*/
|
|
56
|
+
afterLoad?: (dbm: DBM) => Promisable<DBM | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Allows to access the DBM just before it's supposed to be saved to the DB.
|
|
59
|
+
*
|
|
60
|
+
* Normally does nothing.
|
|
61
|
+
*
|
|
62
|
+
* You can change the DBM as you want here: ok to mutate or not, but you need to return the DBM
|
|
63
|
+
* to pass it further.
|
|
64
|
+
*
|
|
65
|
+
* You can return `null` to prevent it from being saved, without throwing an error.
|
|
66
|
+
* `.save` method will then return the BM/DBM as it has entered the method (it **won't** return the null value!).
|
|
67
|
+
*
|
|
68
|
+
* You can do validations as needed here and throw errors, they will be propagated.
|
|
69
|
+
*/
|
|
70
|
+
beforeSave?: (dbm: DBM) => Promisable<DBM | null>;
|
|
44
71
|
/**
|
|
45
72
|
* Called in:
|
|
46
73
|
* - dbmToBM (applied before DBM becomes BM)
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
CommonLogger,
|
|
3
3
|
ErrorMode,
|
|
4
4
|
ObjectWithId,
|
|
5
|
+
Promisable,
|
|
5
6
|
Saved,
|
|
6
7
|
ZodError,
|
|
7
8
|
ZodSchema,
|
|
@@ -54,7 +55,7 @@ export interface CommonDaoHooks<
|
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
57
|
* Called when loading things "as DBM" and validation is not skipped.
|
|
57
|
-
* When loading things
|
|
58
|
+
* When loading things as BM/TM - other hooks get involved instead:
|
|
58
59
|
* - beforeDBMToBM
|
|
59
60
|
* - beforeBMToTM
|
|
60
61
|
*
|
|
@@ -67,6 +68,35 @@ export interface CommonDaoHooks<
|
|
|
67
68
|
beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>
|
|
68
69
|
beforeBMToTM: (bm: BM) => Partial<TM>
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Allows to access the DBM just after it has been loaded from the DB.
|
|
73
|
+
*
|
|
74
|
+
* Normally does nothing.
|
|
75
|
+
*
|
|
76
|
+
* You can change the DBM as you want here: ok to mutate or not, but you need to return the DBM
|
|
77
|
+
* to pass it further.
|
|
78
|
+
*
|
|
79
|
+
* You can return `null` to make it look "not found".
|
|
80
|
+
*
|
|
81
|
+
* You can do validations as needed here and throw errors, they will be propagated.
|
|
82
|
+
*/
|
|
83
|
+
afterLoad?: (dbm: DBM) => Promisable<DBM | null>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Allows to access the DBM just before it's supposed to be saved to the DB.
|
|
87
|
+
*
|
|
88
|
+
* Normally does nothing.
|
|
89
|
+
*
|
|
90
|
+
* You can change the DBM as you want here: ok to mutate or not, but you need to return the DBM
|
|
91
|
+
* to pass it further.
|
|
92
|
+
*
|
|
93
|
+
* You can return `null` to prevent it from being saved, without throwing an error.
|
|
94
|
+
* `.save` method will then return the BM/DBM as it has entered the method (it **won't** return the null value!).
|
|
95
|
+
*
|
|
96
|
+
* You can do validations as needed here and throw errors, they will be propagated.
|
|
97
|
+
*/
|
|
98
|
+
beforeSave?: (dbm: DBM) => Promisable<DBM | null>
|
|
99
|
+
|
|
70
100
|
/**
|
|
71
101
|
* Called in:
|
|
72
102
|
* - dbmToBM (applied before DBM becomes BM)
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
pMap,
|
|
18
18
|
Promisable,
|
|
19
19
|
Saved,
|
|
20
|
+
SKIP,
|
|
21
|
+
UnixTimestampMillisNumber,
|
|
20
22
|
Unsaved,
|
|
21
23
|
ZodSchema,
|
|
22
24
|
ZodValidationError,
|
|
@@ -35,8 +37,6 @@ import {
|
|
|
35
37
|
transformLogProgress,
|
|
36
38
|
transformMap,
|
|
37
39
|
transformMapSimple,
|
|
38
|
-
transformMapSync,
|
|
39
|
-
transformTap,
|
|
40
40
|
writableVoid,
|
|
41
41
|
} from '@naturalcycles/nodejs-lib'
|
|
42
42
|
import { DBLibError } from '../cnst'
|
|
@@ -53,6 +53,7 @@ import { DBTransaction } from '../transaction/dbTransaction'
|
|
|
53
53
|
import {
|
|
54
54
|
CommonDaoCfg,
|
|
55
55
|
CommonDaoCreateOptions,
|
|
56
|
+
CommonDaoHooks,
|
|
56
57
|
CommonDaoLogLevel,
|
|
57
58
|
CommonDaoOptions,
|
|
58
59
|
CommonDaoSaveOptions,
|
|
@@ -99,7 +100,7 @@ export class CommonDao<
|
|
|
99
100
|
anonymize: dbm => dbm,
|
|
100
101
|
onValidationError: err => err,
|
|
101
102
|
...cfg.hooks,
|
|
102
|
-
},
|
|
103
|
+
} satisfies Partial<CommonDaoHooks<BM, DBM, TM, ID>>,
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
if (this.cfg.createId) {
|
|
@@ -131,7 +132,10 @@ export class CommonDao<
|
|
|
131
132
|
const table = opt.table || this.cfg.table
|
|
132
133
|
const started = this.logStarted(op, table)
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
let dbm = (await this.cfg.db.getByIds<DBM>(table, [id]))[0]
|
|
136
|
+
if (dbm && !opt.raw && this.cfg.hooks!.afterLoad) {
|
|
137
|
+
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
138
|
+
}
|
|
135
139
|
|
|
136
140
|
const bm = opt.raw ? (dbm as any) : await this.dbmToBM(dbm, opt)
|
|
137
141
|
this.logResult(started, op, bm, table)
|
|
@@ -161,6 +165,10 @@ export class CommonDao<
|
|
|
161
165
|
const table = opt.table || this.cfg.table
|
|
162
166
|
const started = this.logStarted(op, table)
|
|
163
167
|
let [dbm] = await this.cfg.db.getByIds<DBM>(table, [id])
|
|
168
|
+
if (dbm && !opt.raw && this.cfg.hooks!.afterLoad) {
|
|
169
|
+
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
170
|
+
}
|
|
171
|
+
|
|
164
172
|
if (!opt.raw) {
|
|
165
173
|
dbm = this.anyToDBM(dbm!, opt)
|
|
166
174
|
}
|
|
@@ -175,7 +183,11 @@ export class CommonDao<
|
|
|
175
183
|
const op = `getByIdAsTM(${id})`
|
|
176
184
|
const table = opt.table || this.cfg.table
|
|
177
185
|
const started = this.logStarted(op, table)
|
|
178
|
-
|
|
186
|
+
let [dbm] = await this.cfg.db.getByIds<DBM>(table, [id])
|
|
187
|
+
if (dbm && !opt.raw && this.cfg.hooks!.afterLoad) {
|
|
188
|
+
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
189
|
+
}
|
|
190
|
+
|
|
179
191
|
if (opt.raw) {
|
|
180
192
|
this.logResult(started, op, dbm, table)
|
|
181
193
|
return (dbm as any) || null
|
|
@@ -187,20 +199,34 @@ export class CommonDao<
|
|
|
187
199
|
}
|
|
188
200
|
|
|
189
201
|
async getByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<Saved<BM>[]> {
|
|
202
|
+
if (!ids.length) return []
|
|
190
203
|
const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
191
204
|
const table = opt.table || this.cfg.table
|
|
192
205
|
const started = this.logStarted(op, table)
|
|
193
|
-
|
|
206
|
+
let dbms = await this.cfg.db.getByIds<DBM>(table, ids)
|
|
207
|
+
if (!opt.raw && this.cfg.hooks!.afterLoad && dbms.length) {
|
|
208
|
+
dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
209
|
+
_isTruthy,
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
194
213
|
const bms = opt.raw ? (dbms as any) : await this.dbmsToBM(dbms, opt)
|
|
195
214
|
this.logResult(started, op, bms, table)
|
|
196
215
|
return bms
|
|
197
216
|
}
|
|
198
217
|
|
|
199
218
|
async getByIdsAsDBM(ids: ID[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
|
|
219
|
+
if (!ids.length) return []
|
|
200
220
|
const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
201
221
|
const table = opt.table || this.cfg.table
|
|
202
222
|
const started = this.logStarted(op, table)
|
|
203
|
-
|
|
223
|
+
let dbms = await this.cfg.db.getByIds<DBM>(table, ids)
|
|
224
|
+
if (!opt.raw && this.cfg.hooks!.afterLoad && dbms.length) {
|
|
225
|
+
dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
226
|
+
_isTruthy,
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
|
|
204
230
|
this.logResult(started, op, dbms, table)
|
|
205
231
|
return dbms
|
|
206
232
|
}
|
|
@@ -323,8 +349,14 @@ export class CommonDao<
|
|
|
323
349
|
q.table = opt.table || q.table
|
|
324
350
|
const op = `runQuery(${q.pretty()})`
|
|
325
351
|
const started = this.logStarted(op, q.table)
|
|
326
|
-
|
|
352
|
+
let { rows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
|
|
327
353
|
const partialQuery = !!q._selectedFieldNames
|
|
354
|
+
if (!opt.raw && this.cfg.hooks!.afterLoad && rows.length) {
|
|
355
|
+
rows = (await pMap(rows, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
356
|
+
_isTruthy,
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
|
|
328
360
|
const bms = partialQuery || opt.raw ? (rows as any[]) : await this.dbmsToBM(rows, opt)
|
|
329
361
|
this.logResult(started, op, bms, q.table)
|
|
330
362
|
return {
|
|
@@ -345,7 +377,13 @@ export class CommonDao<
|
|
|
345
377
|
q.table = opt.table || q.table
|
|
346
378
|
const op = `runQueryAsDBM(${q.pretty()})`
|
|
347
379
|
const started = this.logStarted(op, q.table)
|
|
348
|
-
|
|
380
|
+
let { rows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
|
|
381
|
+
if (!opt.raw && this.cfg.hooks!.afterLoad && rows.length) {
|
|
382
|
+
rows = (await pMap(rows, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
383
|
+
_isTruthy,
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
|
|
349
387
|
const partialQuery = !!q._selectedFieldNames
|
|
350
388
|
const dbms = partialQuery || opt.raw ? rows : this.anyToDBMs(rows, opt)
|
|
351
389
|
this.logResult(started, op, dbms, q.table)
|
|
@@ -364,7 +402,13 @@ export class CommonDao<
|
|
|
364
402
|
q.table = opt.table || q.table
|
|
365
403
|
const op = `runQueryAsTM(${q.pretty()})`
|
|
366
404
|
const started = this.logStarted(op, q.table)
|
|
367
|
-
|
|
405
|
+
let { rows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
|
|
406
|
+
if (!opt.raw && this.cfg.hooks!.afterLoad && rows.length) {
|
|
407
|
+
rows = (await pMap(rows, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
408
|
+
_isTruthy,
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
|
|
368
412
|
const partialQuery = !!q._selectedFieldNames
|
|
369
413
|
const tms =
|
|
370
414
|
partialQuery || opt.raw ? (rows as any[]) : this.bmsToTM(await this.dbmsToBM(rows, opt), opt)
|
|
@@ -403,12 +447,17 @@ export class CommonDao<
|
|
|
403
447
|
|
|
404
448
|
await _pipeline([
|
|
405
449
|
this.cfg.db.streamQuery<DBM>(q, opt),
|
|
406
|
-
// optimization: 1 validation is enough
|
|
407
|
-
// transformMap<any, DBM>(dbm => (partialQuery || opt.raw ? dbm : this.anyToDBM(dbm, opt)), opt),
|
|
408
450
|
transformMap<DBM, Saved<BM>>(
|
|
409
451
|
async dbm => {
|
|
410
452
|
count++
|
|
411
|
-
|
|
453
|
+
if (partialQuery || opt.raw) return dbm as any
|
|
454
|
+
|
|
455
|
+
if (this.cfg.hooks!.afterLoad) {
|
|
456
|
+
dbm = (await this.cfg.hooks!.afterLoad(dbm)) as DBM
|
|
457
|
+
if (dbm === null) return SKIP
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return await this.dbmToBM(dbm, opt)
|
|
412
461
|
},
|
|
413
462
|
{
|
|
414
463
|
errorMode: opt.errorMode,
|
|
@@ -448,10 +497,17 @@ export class CommonDao<
|
|
|
448
497
|
|
|
449
498
|
await _pipeline([
|
|
450
499
|
this.cfg.db.streamQuery<any>(q, opt),
|
|
451
|
-
|
|
452
|
-
dbm => {
|
|
500
|
+
transformMap<any, DBM>(
|
|
501
|
+
async dbm => {
|
|
453
502
|
count++
|
|
454
|
-
|
|
503
|
+
if (partialQuery || opt.raw) return dbm
|
|
504
|
+
|
|
505
|
+
if (this.cfg.hooks!.afterLoad) {
|
|
506
|
+
dbm = (await this.cfg.hooks!.afterLoad(dbm)) as DBM
|
|
507
|
+
if (dbm === null) return SKIP
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return this.anyToDBM(dbm, opt)
|
|
455
511
|
},
|
|
456
512
|
{
|
|
457
513
|
errorMode: opt.errorMode,
|
|
@@ -491,9 +547,19 @@ export class CommonDao<
|
|
|
491
547
|
return stream
|
|
492
548
|
.on('error', err => stream.emit('error', err))
|
|
493
549
|
.pipe(
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
550
|
+
transformMap<any, DBM>(
|
|
551
|
+
async dbm => {
|
|
552
|
+
if (this.cfg.hooks!.afterLoad) {
|
|
553
|
+
dbm = (await this.cfg.hooks!.afterLoad(dbm)) as DBM
|
|
554
|
+
if (dbm === null) return SKIP
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return this.anyToDBM(dbm, opt)
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
errorMode: opt.errorMode,
|
|
561
|
+
},
|
|
562
|
+
),
|
|
497
563
|
)
|
|
498
564
|
}
|
|
499
565
|
|
|
@@ -523,9 +589,19 @@ export class CommonDao<
|
|
|
523
589
|
// .pipe(transformMap<DBM, Saved<BM>>(dbm => this.dbmToBM(dbm, opt), safeOpt))
|
|
524
590
|
.on('error', err => stream.emit('error', err))
|
|
525
591
|
.pipe(
|
|
526
|
-
transformMap<DBM, Saved<BM>>(
|
|
527
|
-
|
|
528
|
-
|
|
592
|
+
transformMap<DBM, Saved<BM>>(
|
|
593
|
+
async dbm => {
|
|
594
|
+
if (this.cfg.hooks!.afterLoad) {
|
|
595
|
+
dbm = (await this.cfg.hooks!.afterLoad(dbm)) as DBM
|
|
596
|
+
if (dbm === null) return SKIP
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return await this.dbmToBM(dbm, opt)
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
errorMode: opt.errorMode,
|
|
603
|
+
},
|
|
604
|
+
),
|
|
529
605
|
)
|
|
530
606
|
// this can make the stream async-iteration-friendly
|
|
531
607
|
// but not applying it now for perf reasons
|
|
@@ -569,8 +645,10 @@ export class CommonDao<
|
|
|
569
645
|
|
|
570
646
|
await _pipeline([
|
|
571
647
|
this.cfg.db.streamQuery<DBM>(q.select(['id']), opt),
|
|
572
|
-
transformMapSimple<DBM, ID>(objectWithId =>
|
|
573
|
-
|
|
648
|
+
transformMapSimple<DBM, ID>(objectWithId => {
|
|
649
|
+
count++
|
|
650
|
+
return objectWithId.id
|
|
651
|
+
}),
|
|
574
652
|
transformMap<ID, void>(mapper, {
|
|
575
653
|
...opt,
|
|
576
654
|
predicate: _passthroughPredicate,
|
|
@@ -618,8 +696,10 @@ export class CommonDao<
|
|
|
618
696
|
save: async (
|
|
619
697
|
bm: Unsaved<BM>,
|
|
620
698
|
opt: CommonDaoSaveOptions<DBM> = {},
|
|
621
|
-
): Promise<DBSaveBatchOperation> => {
|
|
622
|
-
|
|
699
|
+
): Promise<DBSaveBatchOperation | undefined> => {
|
|
700
|
+
// .save actually returns DBM (not BM) when it detects `opt.tx === true`
|
|
701
|
+
const row: DBM | null = (await this.save(bm, { ...opt, tx: true })) as any
|
|
702
|
+
if (row === null) return
|
|
623
703
|
|
|
624
704
|
return {
|
|
625
705
|
type: 'saveBatch',
|
|
@@ -635,8 +715,8 @@ export class CommonDao<
|
|
|
635
715
|
bms: Unsaved<BM>[],
|
|
636
716
|
opt: CommonDaoSaveOptions<DBM> = {},
|
|
637
717
|
): Promise<DBSaveBatchOperation | undefined> => {
|
|
638
|
-
if (!bms.length) return
|
|
639
718
|
const rows: DBM[] = (await this.saveBatch(bms, { ...opt, tx: true })) as any
|
|
719
|
+
if (!rows.length) return
|
|
640
720
|
|
|
641
721
|
return {
|
|
642
722
|
type: 'saveBatch',
|
|
@@ -682,9 +762,15 @@ export class CommonDao<
|
|
|
682
762
|
this.requireWriteAccess()
|
|
683
763
|
const idWasGenerated = !bm.id && this.cfg.createId
|
|
684
764
|
this.assignIdCreatedUpdated(bm, opt) // mutates
|
|
685
|
-
|
|
765
|
+
let dbm = await this.bmToDBM(bm as BM, opt)
|
|
766
|
+
|
|
767
|
+
if (this.cfg.hooks!.beforeSave) {
|
|
768
|
+
dbm = (await this.cfg.hooks!.beforeSave(dbm)) as DBM
|
|
769
|
+
if (dbm === null && !opt.tx) return bm as any
|
|
770
|
+
}
|
|
686
771
|
|
|
687
772
|
if (opt.tx) {
|
|
773
|
+
// May return `null`, in which case it'll be skipped
|
|
688
774
|
return dbm as any
|
|
689
775
|
}
|
|
690
776
|
|
|
@@ -697,6 +783,7 @@ export class CommonDao<
|
|
|
697
783
|
const started = this.logSaveStarted(op, bm, table)
|
|
698
784
|
const { excludeFromIndexes } = this.cfg
|
|
699
785
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
786
|
+
|
|
700
787
|
await this.cfg.db.saveBatch(table, [dbm], {
|
|
701
788
|
excludeFromIndexes,
|
|
702
789
|
assignGeneratedIds,
|
|
@@ -763,6 +850,12 @@ export class CommonDao<
|
|
|
763
850
|
const started = this.logSaveStarted(op, row, table)
|
|
764
851
|
const { excludeFromIndexes } = this.cfg
|
|
765
852
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
853
|
+
|
|
854
|
+
if (this.cfg.hooks!.beforeSave) {
|
|
855
|
+
row = (await this.cfg.hooks!.beforeSave(row)) as DBM
|
|
856
|
+
if (row === null) return dbm
|
|
857
|
+
}
|
|
858
|
+
|
|
766
859
|
await this.cfg.db.saveBatch(table, [row], {
|
|
767
860
|
excludeFromIndexes,
|
|
768
861
|
assignGeneratedIds,
|
|
@@ -778,10 +871,17 @@ export class CommonDao<
|
|
|
778
871
|
}
|
|
779
872
|
|
|
780
873
|
async saveBatch(bms: Unsaved<BM>[], opt: CommonDaoSaveOptions<DBM> = {}): Promise<Saved<BM>[]> {
|
|
874
|
+
if (!bms.length) return []
|
|
781
875
|
this.requireWriteAccess()
|
|
782
876
|
const table = opt.table || this.cfg.table
|
|
783
877
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt))
|
|
784
|
-
|
|
878
|
+
let dbms = await this.bmsToDBM(bms as BM[], opt)
|
|
879
|
+
|
|
880
|
+
if (this.cfg.hooks!.beforeSave && dbms.length) {
|
|
881
|
+
dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.beforeSave!(dbm))).filter(
|
|
882
|
+
_isTruthy,
|
|
883
|
+
)
|
|
884
|
+
}
|
|
785
885
|
|
|
786
886
|
if (opt.tx) {
|
|
787
887
|
return dbms as any
|
|
@@ -802,6 +902,7 @@ export class CommonDao<
|
|
|
802
902
|
const started = this.logSaveStarted(op, bms, table)
|
|
803
903
|
const { excludeFromIndexes } = this.cfg
|
|
804
904
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
905
|
+
|
|
805
906
|
await this.cfg.db.saveBatch(table, dbms, {
|
|
806
907
|
excludeFromIndexes,
|
|
807
908
|
assignGeneratedIds,
|
|
@@ -818,6 +919,7 @@ export class CommonDao<
|
|
|
818
919
|
}
|
|
819
920
|
|
|
820
921
|
async saveBatchAsDBM(dbms: DBM[], opt: CommonDaoSaveOptions<DBM> = {}): Promise<DBM[]> {
|
|
922
|
+
if (!dbms.length) return []
|
|
821
923
|
this.requireWriteAccess()
|
|
822
924
|
const table = opt.table || this.cfg.table
|
|
823
925
|
let rows = dbms
|
|
@@ -840,6 +942,12 @@ export class CommonDao<
|
|
|
840
942
|
const { excludeFromIndexes } = this.cfg
|
|
841
943
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
842
944
|
|
|
945
|
+
if (this.cfg.hooks!.beforeSave && rows.length) {
|
|
946
|
+
rows = (await pMap(rows, async row => await this.cfg.hooks!.beforeSave!(row))).filter(
|
|
947
|
+
_isTruthy,
|
|
948
|
+
)
|
|
949
|
+
}
|
|
950
|
+
|
|
843
951
|
await this.cfg.db.saveBatch(table, rows, {
|
|
844
952
|
excludeFromIndexes,
|
|
845
953
|
assignGeneratedIds,
|
|
@@ -873,6 +981,7 @@ export class CommonDao<
|
|
|
873
981
|
}
|
|
874
982
|
|
|
875
983
|
async deleteByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<number> {
|
|
984
|
+
if (!ids.length) return 0
|
|
876
985
|
this.requireWriteAccess()
|
|
877
986
|
this.requireObjectMutability(opt)
|
|
878
987
|
const op = `deleteByIds(${ids.join(', ')})`
|
|
@@ -941,6 +1050,7 @@ export class CommonDao<
|
|
|
941
1050
|
}
|
|
942
1051
|
|
|
943
1052
|
async updateByIds(ids: ID[], patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
|
|
1053
|
+
if (!ids.length) return 0
|
|
944
1054
|
return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt)
|
|
945
1055
|
}
|
|
946
1056
|
|
|
@@ -1187,14 +1297,14 @@ export class CommonDao<
|
|
|
1187
1297
|
this.cfg.logger?.log(`<< ${table}.${op} in ${_since(started)}`)
|
|
1188
1298
|
}
|
|
1189
1299
|
|
|
1190
|
-
protected logStarted(op: string, table: string, force = false):
|
|
1300
|
+
protected logStarted(op: string, table: string, force = false): UnixTimestampMillisNumber {
|
|
1191
1301
|
if (this.cfg.logStarted || force) {
|
|
1192
1302
|
this.cfg.logger?.log(`>> ${table}.${op}`)
|
|
1193
1303
|
}
|
|
1194
1304
|
return Date.now()
|
|
1195
1305
|
}
|
|
1196
1306
|
|
|
1197
|
-
protected logSaveStarted(op: string, items: any, table: string):
|
|
1307
|
+
protected logSaveStarted(op: string, items: any, table: string): UnixTimestampMillisNumber {
|
|
1198
1308
|
if (this.cfg.logStarted) {
|
|
1199
1309
|
const args: any[] = [`>> ${table}.${op}`]
|
|
1200
1310
|
if (Array.isArray(items)) {
|