@naturalcycles/db-lib 8.52.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>;
|
|
@@ -139,9 +139,6 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
|
|
|
139
139
|
bmToTM(bm: undefined, opt?: CommonDaoOptions): TM | undefined;
|
|
140
140
|
bmToTM(bm?: Saved<BM>, opt?: CommonDaoOptions): TM;
|
|
141
141
|
bmsToTM(bms: Saved<BM>[], opt?: CommonDaoOptions): TM[];
|
|
142
|
-
tmToBM(tm: undefined, opt?: CommonDaoOptions): undefined;
|
|
143
|
-
tmToBM(tm?: TM, opt?: CommonDaoOptions): BM;
|
|
144
|
-
tmsToBM(tms: TM[], opt?: CommonDaoOptions): BM[];
|
|
145
142
|
/**
|
|
146
143
|
* Returns *converted value*.
|
|
147
144
|
* Validates (unless `skipValidation=true` passed).
|
|
@@ -158,6 +155,6 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
|
|
|
158
155
|
runInTransaction(ops: Promisable<DBOperation | undefined>[]): Promise<void>;
|
|
159
156
|
protected logResult(started: number, op: string, res: any, table: string): void;
|
|
160
157
|
protected logSaveResult(started: number, op: string, table: string): void;
|
|
161
|
-
protected logStarted(op: string, table: string, force?: boolean):
|
|
162
|
-
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;
|
|
163
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,
|
|
@@ -86,7 +89,6 @@ class CommonDao {
|
|
|
86
89
|
beforeDBMValidate: dbm => dbm,
|
|
87
90
|
beforeDBMToBM: dbm => dbm,
|
|
88
91
|
beforeBMToDBM: bm => bm,
|
|
89
|
-
beforeTMToBM: tm => tm,
|
|
90
92
|
beforeBMToTM: bm => bm,
|
|
91
93
|
anonymize: dbm => dbm,
|
|
92
94
|
onValidationError: err => err,
|
|
@@ -114,7 +116,10 @@ class CommonDao {
|
|
|
114
116
|
const op = `getById(${id})`;
|
|
115
117
|
const table = opt.table || this.cfg.table;
|
|
116
118
|
const started = this.logStarted(op, table);
|
|
117
|
-
|
|
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
|
+
}
|
|
118
123
|
const bm = opt.raw ? dbm : await this.dbmToBM(dbm, opt);
|
|
119
124
|
this.logResult(started, op, bm, table);
|
|
120
125
|
return bm || null;
|
|
@@ -139,6 +144,9 @@ class CommonDao {
|
|
|
139
144
|
const table = opt.table || this.cfg.table;
|
|
140
145
|
const started = this.logStarted(op, table);
|
|
141
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
|
+
}
|
|
142
150
|
if (!opt.raw) {
|
|
143
151
|
dbm = this.anyToDBM(dbm, opt);
|
|
144
152
|
}
|
|
@@ -151,7 +159,10 @@ class CommonDao {
|
|
|
151
159
|
const op = `getByIdAsTM(${id})`;
|
|
152
160
|
const table = opt.table || this.cfg.table;
|
|
153
161
|
const started = this.logStarted(op, table);
|
|
154
|
-
|
|
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
|
+
}
|
|
155
166
|
if (opt.raw) {
|
|
156
167
|
this.logResult(started, op, dbm, table);
|
|
157
168
|
return dbm || null;
|
|
@@ -162,19 +173,29 @@ class CommonDao {
|
|
|
162
173
|
return tm || null;
|
|
163
174
|
}
|
|
164
175
|
async getByIds(ids, opt = {}) {
|
|
176
|
+
if (!ids.length)
|
|
177
|
+
return [];
|
|
165
178
|
const op = `getByIds ${ids.length} id(s) (${(0, js_lib_1._truncate)(ids.slice(0, 10).join(', '), 50)})`;
|
|
166
179
|
const table = opt.table || this.cfg.table;
|
|
167
180
|
const started = this.logStarted(op, table);
|
|
168
|
-
|
|
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
|
+
}
|
|
169
185
|
const bms = opt.raw ? dbms : await this.dbmsToBM(dbms, opt);
|
|
170
186
|
this.logResult(started, op, bms, table);
|
|
171
187
|
return bms;
|
|
172
188
|
}
|
|
173
189
|
async getByIdsAsDBM(ids, opt = {}) {
|
|
190
|
+
if (!ids.length)
|
|
191
|
+
return [];
|
|
174
192
|
const op = `getByIdsAsDBM ${ids.length} id(s) (${(0, js_lib_1._truncate)(ids.slice(0, 10).join(', '), 50)})`;
|
|
175
193
|
const table = opt.table || this.cfg.table;
|
|
176
194
|
const started = this.logStarted(op, table);
|
|
177
|
-
|
|
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
|
+
}
|
|
178
199
|
this.logResult(started, op, dbms, table);
|
|
179
200
|
return dbms;
|
|
180
201
|
}
|
|
@@ -273,8 +294,11 @@ class CommonDao {
|
|
|
273
294
|
q.table = opt.table || q.table;
|
|
274
295
|
const op = `runQuery(${q.pretty()})`;
|
|
275
296
|
const started = this.logStarted(op, q.table);
|
|
276
|
-
|
|
297
|
+
let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
277
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
|
+
}
|
|
278
302
|
const bms = partialQuery || opt.raw ? rows : await this.dbmsToBM(rows, opt);
|
|
279
303
|
this.logResult(started, op, bms, q.table);
|
|
280
304
|
return {
|
|
@@ -290,7 +314,10 @@ class CommonDao {
|
|
|
290
314
|
q.table = opt.table || q.table;
|
|
291
315
|
const op = `runQueryAsDBM(${q.pretty()})`;
|
|
292
316
|
const started = this.logStarted(op, q.table);
|
|
293
|
-
|
|
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
|
+
}
|
|
294
321
|
const partialQuery = !!q._selectedFieldNames;
|
|
295
322
|
const dbms = partialQuery || opt.raw ? rows : this.anyToDBMs(rows, opt);
|
|
296
323
|
this.logResult(started, op, dbms, q.table);
|
|
@@ -304,7 +331,10 @@ class CommonDao {
|
|
|
304
331
|
q.table = opt.table || q.table;
|
|
305
332
|
const op = `runQueryAsTM(${q.pretty()})`;
|
|
306
333
|
const started = this.logStarted(op, q.table);
|
|
307
|
-
|
|
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
|
+
}
|
|
308
338
|
const partialQuery = !!q._selectedFieldNames;
|
|
309
339
|
const tms = partialQuery || opt.raw ? rows : this.bmsToTM(await this.dbmsToBM(rows, opt), opt);
|
|
310
340
|
this.logResult(started, op, tms, q.table);
|
|
@@ -334,11 +364,16 @@ class CommonDao {
|
|
|
334
364
|
let count = 0;
|
|
335
365
|
await (0, nodejs_lib_1._pipeline)([
|
|
336
366
|
this.cfg.db.streamQuery(q, opt),
|
|
337
|
-
// optimization: 1 validation is enough
|
|
338
|
-
// transformMap<any, DBM>(dbm => (partialQuery || opt.raw ? dbm : this.anyToDBM(dbm, opt)), opt),
|
|
339
367
|
(0, nodejs_lib_1.transformMap)(async (dbm) => {
|
|
340
368
|
count++;
|
|
341
|
-
|
|
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);
|
|
342
377
|
}, {
|
|
343
378
|
errorMode: opt.errorMode,
|
|
344
379
|
}),
|
|
@@ -368,9 +403,16 @@ class CommonDao {
|
|
|
368
403
|
let count = 0;
|
|
369
404
|
await (0, nodejs_lib_1._pipeline)([
|
|
370
405
|
this.cfg.db.streamQuery(q, opt),
|
|
371
|
-
(0, nodejs_lib_1.
|
|
406
|
+
(0, nodejs_lib_1.transformMap)(async (dbm) => {
|
|
372
407
|
count++;
|
|
373
|
-
|
|
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);
|
|
374
416
|
}, {
|
|
375
417
|
errorMode: opt.errorMode,
|
|
376
418
|
}),
|
|
@@ -403,8 +445,15 @@ class CommonDao {
|
|
|
403
445
|
return stream;
|
|
404
446
|
return stream
|
|
405
447
|
.on('error', err => stream.emit('error', err))
|
|
406
|
-
.pipe((0, nodejs_lib_1.
|
|
407
|
-
|
|
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,
|
|
408
457
|
}));
|
|
409
458
|
}
|
|
410
459
|
/**
|
|
@@ -430,8 +479,15 @@ class CommonDao {
|
|
|
430
479
|
// .pipe(transformMap<any, DBM>(dbm => this.anyToDBM(dbm, opt), safeOpt))
|
|
431
480
|
// .pipe(transformMap<DBM, Saved<BM>>(dbm => this.dbmToBM(dbm, opt), safeOpt))
|
|
432
481
|
.on('error', err => stream.emit('error', err))
|
|
433
|
-
.pipe((0, nodejs_lib_1.transformMap)(async (dbm) =>
|
|
434
|
-
|
|
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,
|
|
435
491
|
}))
|
|
436
492
|
// this can make the stream async-iteration-friendly
|
|
437
493
|
// but not applying it now for perf reasons
|
|
@@ -462,8 +518,10 @@ class CommonDao {
|
|
|
462
518
|
let count = 0;
|
|
463
519
|
await (0, nodejs_lib_1._pipeline)([
|
|
464
520
|
this.cfg.db.streamQuery(q.select(['id']), opt),
|
|
465
|
-
(0, nodejs_lib_1.transformMapSimple)(objectWithId =>
|
|
466
|
-
|
|
521
|
+
(0, nodejs_lib_1.transformMapSimple)(objectWithId => {
|
|
522
|
+
count++;
|
|
523
|
+
return objectWithId.id;
|
|
524
|
+
}),
|
|
467
525
|
(0, nodejs_lib_1.transformMap)(mapper, {
|
|
468
526
|
...opt,
|
|
469
527
|
predicate: js_lib_1._passthroughPredicate,
|
|
@@ -503,8 +561,14 @@ class CommonDao {
|
|
|
503
561
|
this.requireWriteAccess();
|
|
504
562
|
const idWasGenerated = !bm.id && this.cfg.createId;
|
|
505
563
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
506
|
-
|
|
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
|
+
}
|
|
507
570
|
if (opt.tx) {
|
|
571
|
+
// May return `null`, in which case it'll be skipped
|
|
508
572
|
return dbm;
|
|
509
573
|
}
|
|
510
574
|
const table = opt.table || this.cfg.table;
|
|
@@ -570,6 +634,11 @@ class CommonDao {
|
|
|
570
634
|
const started = this.logSaveStarted(op, row, table);
|
|
571
635
|
const { excludeFromIndexes } = this.cfg;
|
|
572
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
|
+
}
|
|
573
642
|
await this.cfg.db.saveBatch(table, [row], {
|
|
574
643
|
excludeFromIndexes,
|
|
575
644
|
assignGeneratedIds,
|
|
@@ -582,10 +651,15 @@ class CommonDao {
|
|
|
582
651
|
return row;
|
|
583
652
|
}
|
|
584
653
|
async saveBatch(bms, opt = {}) {
|
|
654
|
+
if (!bms.length)
|
|
655
|
+
return [];
|
|
585
656
|
this.requireWriteAccess();
|
|
586
657
|
const table = opt.table || this.cfg.table;
|
|
587
658
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt));
|
|
588
|
-
|
|
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
|
+
}
|
|
589
663
|
if (opt.tx) {
|
|
590
664
|
return dbms;
|
|
591
665
|
}
|
|
@@ -613,6 +687,8 @@ class CommonDao {
|
|
|
613
687
|
return bms;
|
|
614
688
|
}
|
|
615
689
|
async saveBatchAsDBM(dbms, opt = {}) {
|
|
690
|
+
if (!dbms.length)
|
|
691
|
+
return [];
|
|
616
692
|
this.requireWriteAccess();
|
|
617
693
|
const table = opt.table || this.cfg.table;
|
|
618
694
|
let rows = dbms;
|
|
@@ -632,6 +708,9 @@ class CommonDao {
|
|
|
632
708
|
const started = this.logSaveStarted(op, rows, table);
|
|
633
709
|
const { excludeFromIndexes } = this.cfg;
|
|
634
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
|
+
}
|
|
635
714
|
await this.cfg.db.saveBatch(table, rows, {
|
|
636
715
|
excludeFromIndexes,
|
|
637
716
|
assignGeneratedIds,
|
|
@@ -656,6 +735,8 @@ class CommonDao {
|
|
|
656
735
|
return count;
|
|
657
736
|
}
|
|
658
737
|
async deleteByIds(ids, opt = {}) {
|
|
738
|
+
if (!ids.length)
|
|
739
|
+
return 0;
|
|
659
740
|
this.requireWriteAccess();
|
|
660
741
|
this.requireObjectMutability(opt);
|
|
661
742
|
const op = `deleteByIds(${ids.join(', ')})`;
|
|
@@ -710,6 +791,8 @@ class CommonDao {
|
|
|
710
791
|
return await this.updateByQuery(this.query().filterEq('id', id), patch, opt);
|
|
711
792
|
}
|
|
712
793
|
async updateByIds(ids, patch, opt = {}) {
|
|
794
|
+
if (!ids.length)
|
|
795
|
+
return 0;
|
|
713
796
|
return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt);
|
|
714
797
|
}
|
|
715
798
|
async updateByQuery(q, patch, opt = {}) {
|
|
@@ -790,22 +873,6 @@ class CommonDao {
|
|
|
790
873
|
// try/catch?
|
|
791
874
|
return bms.map(bm => this.bmToTM(bm, opt));
|
|
792
875
|
}
|
|
793
|
-
tmToBM(tm, opt = {}) {
|
|
794
|
-
if (!tm)
|
|
795
|
-
return;
|
|
796
|
-
// optimization: 1 validation is enough
|
|
797
|
-
// Validate/convert TM
|
|
798
|
-
// bm gets assigned to the new reference
|
|
799
|
-
// tm = this.validateAndConvert(tm, this.cfg.tmSchema, DBModelType.TM, opt)
|
|
800
|
-
// TM > BM
|
|
801
|
-
const bm = this.cfg.hooks.beforeTMToBM(tm);
|
|
802
|
-
// Validate/convert BM
|
|
803
|
-
return this.validateAndConvert(bm, this.cfg.bmSchema, db_model_1.DBModelType.BM, opt);
|
|
804
|
-
}
|
|
805
|
-
tmsToBM(tms, opt = {}) {
|
|
806
|
-
// try/catch?
|
|
807
|
-
return tms.map(tm => this.tmToBM(tm, opt));
|
|
808
|
-
}
|
|
809
876
|
/**
|
|
810
877
|
* Returns *converted value*.
|
|
811
878
|
* Validates (unless `skipValidation=true` passed).
|
|
@@ -1,21 +1,82 @@
|
|
|
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';
|
|
5
5
|
export interface CommonDaoHooks<BM extends Partial<ObjectWithId<ID>>, DBM extends ObjectWithId<ID>, TM, ID extends string | number> {
|
|
6
|
+
/**
|
|
7
|
+
* Allows to override the id generation function.
|
|
8
|
+
* By default it uses `stringId` from nodejs-lib
|
|
9
|
+
* (which uses lowercase alphanumberic alphabet and the size of 16).
|
|
10
|
+
*/
|
|
6
11
|
createRandomId: () => ID;
|
|
7
12
|
/**
|
|
8
13
|
* createNaturalId hook is called (tried) first.
|
|
9
14
|
* If it doesn't exist - createRandomId is called.
|
|
10
15
|
*/
|
|
11
16
|
createNaturalId: (obj: DBM | BM) => ID;
|
|
17
|
+
/**
|
|
18
|
+
* It's a counter-part of `createNaturalId`.
|
|
19
|
+
* Allows to provide a parser function to parse "natural id" into
|
|
20
|
+
* DBM components (e.g accountId and some other property that is part of the id).
|
|
21
|
+
*/
|
|
12
22
|
parseNaturalId: (id: ID) => Partial<DBM>;
|
|
23
|
+
/**
|
|
24
|
+
* It is called only on `dao.create` method.
|
|
25
|
+
* Dao.create method is called in:
|
|
26
|
+
*
|
|
27
|
+
* - getByIdOrEmpty, getByIdAsDBMOrEmpty
|
|
28
|
+
* - patch, patchAsDBM
|
|
29
|
+
*/
|
|
13
30
|
beforeCreate: (bm: Partial<BM>) => Partial<BM>;
|
|
31
|
+
/**
|
|
32
|
+
* Called when loading things "as DBM" and validation is not skipped.
|
|
33
|
+
* When loading things as BM/TM - other hooks get involved instead:
|
|
34
|
+
* - beforeDBMToBM
|
|
35
|
+
* - beforeBMToTM
|
|
36
|
+
*
|
|
37
|
+
* TODO: maybe rename those to `validateAs(model)`
|
|
38
|
+
* as it only validates "final state", not intermediate
|
|
39
|
+
*/
|
|
14
40
|
beforeDBMValidate: (dbm: Partial<DBM>) => Partial<DBM>;
|
|
15
41
|
beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>;
|
|
16
42
|
beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>;
|
|
17
|
-
beforeTMToBM: (tm: TM) => Partial<BM>;
|
|
18
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>;
|
|
71
|
+
/**
|
|
72
|
+
* Called in:
|
|
73
|
+
* - dbmToBM (applied before DBM becomes BM)
|
|
74
|
+
* - anyToDBM
|
|
75
|
+
*
|
|
76
|
+
* Hook only allows to apply anonymization to DBM (not to BM).
|
|
77
|
+
* It still applies to BM "transitively", during dbmToBM
|
|
78
|
+
* (e.g after loaded from the Database).
|
|
79
|
+
*/
|
|
19
80
|
anonymize: (dbm: DBM) => DBM;
|
|
20
81
|
/**
|
|
21
82
|
* If hook is defined - allows to prevent or modify the error thrown.
|
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,
|
|
@@ -23,19 +24,88 @@ export interface CommonDaoHooks<
|
|
|
23
24
|
TM,
|
|
24
25
|
ID extends string | number,
|
|
25
26
|
> {
|
|
27
|
+
/**
|
|
28
|
+
* Allows to override the id generation function.
|
|
29
|
+
* By default it uses `stringId` from nodejs-lib
|
|
30
|
+
* (which uses lowercase alphanumberic alphabet and the size of 16).
|
|
31
|
+
*/
|
|
26
32
|
createRandomId: () => ID
|
|
33
|
+
|
|
27
34
|
/**
|
|
28
35
|
* createNaturalId hook is called (tried) first.
|
|
29
36
|
* If it doesn't exist - createRandomId is called.
|
|
30
37
|
*/
|
|
31
38
|
createNaturalId: (obj: DBM | BM) => ID
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* It's a counter-part of `createNaturalId`.
|
|
42
|
+
* Allows to provide a parser function to parse "natural id" into
|
|
43
|
+
* DBM components (e.g accountId and some other property that is part of the id).
|
|
44
|
+
*/
|
|
32
45
|
parseNaturalId: (id: ID) => Partial<DBM>
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* It is called only on `dao.create` method.
|
|
49
|
+
* Dao.create method is called in:
|
|
50
|
+
*
|
|
51
|
+
* - getByIdOrEmpty, getByIdAsDBMOrEmpty
|
|
52
|
+
* - patch, patchAsDBM
|
|
53
|
+
*/
|
|
33
54
|
beforeCreate: (bm: Partial<BM>) => Partial<BM>
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Called when loading things "as DBM" and validation is not skipped.
|
|
58
|
+
* When loading things as BM/TM - other hooks get involved instead:
|
|
59
|
+
* - beforeDBMToBM
|
|
60
|
+
* - beforeBMToTM
|
|
61
|
+
*
|
|
62
|
+
* TODO: maybe rename those to `validateAs(model)`
|
|
63
|
+
* as it only validates "final state", not intermediate
|
|
64
|
+
*/
|
|
34
65
|
beforeDBMValidate: (dbm: Partial<DBM>) => Partial<DBM>
|
|
66
|
+
|
|
35
67
|
beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>
|
|
36
68
|
beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>
|
|
37
|
-
beforeTMToBM: (tm: TM) => Partial<BM>
|
|
38
69
|
beforeBMToTM: (bm: BM) => Partial<TM>
|
|
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
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Called in:
|
|
102
|
+
* - dbmToBM (applied before DBM becomes BM)
|
|
103
|
+
* - anyToDBM
|
|
104
|
+
*
|
|
105
|
+
* Hook only allows to apply anonymization to DBM (not to BM).
|
|
106
|
+
* It still applies to BM "transitively", during dbmToBM
|
|
107
|
+
* (e.g after loaded from the Database).
|
|
108
|
+
*/
|
|
39
109
|
anonymize: (dbm: DBM) => DBM
|
|
40
110
|
|
|
41
111
|
/**
|
|
@@ -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,
|
|
@@ -95,12 +96,11 @@ export class CommonDao<
|
|
|
95
96
|
beforeDBMValidate: dbm => dbm,
|
|
96
97
|
beforeDBMToBM: dbm => dbm as any,
|
|
97
98
|
beforeBMToDBM: bm => bm as any,
|
|
98
|
-
beforeTMToBM: tm => tm as any,
|
|
99
99
|
beforeBMToTM: bm => bm as any,
|
|
100
100
|
anonymize: dbm => dbm,
|
|
101
101
|
onValidationError: err => err,
|
|
102
102
|
...cfg.hooks,
|
|
103
|
-
},
|
|
103
|
+
} satisfies Partial<CommonDaoHooks<BM, DBM, TM, ID>>,
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
if (this.cfg.createId) {
|
|
@@ -132,7 +132,10 @@ export class CommonDao<
|
|
|
132
132
|
const table = opt.table || this.cfg.table
|
|
133
133
|
const started = this.logStarted(op, table)
|
|
134
134
|
|
|
135
|
-
|
|
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
|
+
}
|
|
136
139
|
|
|
137
140
|
const bm = opt.raw ? (dbm as any) : await this.dbmToBM(dbm, opt)
|
|
138
141
|
this.logResult(started, op, bm, table)
|
|
@@ -162,6 +165,10 @@ export class CommonDao<
|
|
|
162
165
|
const table = opt.table || this.cfg.table
|
|
163
166
|
const started = this.logStarted(op, table)
|
|
164
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
|
+
|
|
165
172
|
if (!opt.raw) {
|
|
166
173
|
dbm = this.anyToDBM(dbm!, opt)
|
|
167
174
|
}
|
|
@@ -176,7 +183,11 @@ export class CommonDao<
|
|
|
176
183
|
const op = `getByIdAsTM(${id})`
|
|
177
184
|
const table = opt.table || this.cfg.table
|
|
178
185
|
const started = this.logStarted(op, table)
|
|
179
|
-
|
|
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
|
+
|
|
180
191
|
if (opt.raw) {
|
|
181
192
|
this.logResult(started, op, dbm, table)
|
|
182
193
|
return (dbm as any) || null
|
|
@@ -188,20 +199,34 @@ export class CommonDao<
|
|
|
188
199
|
}
|
|
189
200
|
|
|
190
201
|
async getByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<Saved<BM>[]> {
|
|
202
|
+
if (!ids.length) return []
|
|
191
203
|
const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
192
204
|
const table = opt.table || this.cfg.table
|
|
193
205
|
const started = this.logStarted(op, table)
|
|
194
|
-
|
|
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
|
+
|
|
195
213
|
const bms = opt.raw ? (dbms as any) : await this.dbmsToBM(dbms, opt)
|
|
196
214
|
this.logResult(started, op, bms, table)
|
|
197
215
|
return bms
|
|
198
216
|
}
|
|
199
217
|
|
|
200
218
|
async getByIdsAsDBM(ids: ID[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
|
|
219
|
+
if (!ids.length) return []
|
|
201
220
|
const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
202
221
|
const table = opt.table || this.cfg.table
|
|
203
222
|
const started = this.logStarted(op, table)
|
|
204
|
-
|
|
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
|
+
|
|
205
230
|
this.logResult(started, op, dbms, table)
|
|
206
231
|
return dbms
|
|
207
232
|
}
|
|
@@ -324,8 +349,14 @@ export class CommonDao<
|
|
|
324
349
|
q.table = opt.table || q.table
|
|
325
350
|
const op = `runQuery(${q.pretty()})`
|
|
326
351
|
const started = this.logStarted(op, q.table)
|
|
327
|
-
|
|
352
|
+
let { rows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
|
|
328
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
|
+
|
|
329
360
|
const bms = partialQuery || opt.raw ? (rows as any[]) : await this.dbmsToBM(rows, opt)
|
|
330
361
|
this.logResult(started, op, bms, q.table)
|
|
331
362
|
return {
|
|
@@ -346,7 +377,13 @@ export class CommonDao<
|
|
|
346
377
|
q.table = opt.table || q.table
|
|
347
378
|
const op = `runQueryAsDBM(${q.pretty()})`
|
|
348
379
|
const started = this.logStarted(op, q.table)
|
|
349
|
-
|
|
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
|
+
|
|
350
387
|
const partialQuery = !!q._selectedFieldNames
|
|
351
388
|
const dbms = partialQuery || opt.raw ? rows : this.anyToDBMs(rows, opt)
|
|
352
389
|
this.logResult(started, op, dbms, q.table)
|
|
@@ -365,7 +402,13 @@ export class CommonDao<
|
|
|
365
402
|
q.table = opt.table || q.table
|
|
366
403
|
const op = `runQueryAsTM(${q.pretty()})`
|
|
367
404
|
const started = this.logStarted(op, q.table)
|
|
368
|
-
|
|
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
|
+
|
|
369
412
|
const partialQuery = !!q._selectedFieldNames
|
|
370
413
|
const tms =
|
|
371
414
|
partialQuery || opt.raw ? (rows as any[]) : this.bmsToTM(await this.dbmsToBM(rows, opt), opt)
|
|
@@ -404,12 +447,17 @@ export class CommonDao<
|
|
|
404
447
|
|
|
405
448
|
await _pipeline([
|
|
406
449
|
this.cfg.db.streamQuery<DBM>(q, opt),
|
|
407
|
-
// optimization: 1 validation is enough
|
|
408
|
-
// transformMap<any, DBM>(dbm => (partialQuery || opt.raw ? dbm : this.anyToDBM(dbm, opt)), opt),
|
|
409
450
|
transformMap<DBM, Saved<BM>>(
|
|
410
451
|
async dbm => {
|
|
411
452
|
count++
|
|
412
|
-
|
|
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)
|
|
413
461
|
},
|
|
414
462
|
{
|
|
415
463
|
errorMode: opt.errorMode,
|
|
@@ -449,10 +497,17 @@ export class CommonDao<
|
|
|
449
497
|
|
|
450
498
|
await _pipeline([
|
|
451
499
|
this.cfg.db.streamQuery<any>(q, opt),
|
|
452
|
-
|
|
453
|
-
dbm => {
|
|
500
|
+
transformMap<any, DBM>(
|
|
501
|
+
async dbm => {
|
|
454
502
|
count++
|
|
455
|
-
|
|
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)
|
|
456
511
|
},
|
|
457
512
|
{
|
|
458
513
|
errorMode: opt.errorMode,
|
|
@@ -492,9 +547,19 @@ export class CommonDao<
|
|
|
492
547
|
return stream
|
|
493
548
|
.on('error', err => stream.emit('error', err))
|
|
494
549
|
.pipe(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
+
),
|
|
498
563
|
)
|
|
499
564
|
}
|
|
500
565
|
|
|
@@ -524,9 +589,19 @@ export class CommonDao<
|
|
|
524
589
|
// .pipe(transformMap<DBM, Saved<BM>>(dbm => this.dbmToBM(dbm, opt), safeOpt))
|
|
525
590
|
.on('error', err => stream.emit('error', err))
|
|
526
591
|
.pipe(
|
|
527
|
-
transformMap<DBM, Saved<BM>>(
|
|
528
|
-
|
|
529
|
-
|
|
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
|
+
),
|
|
530
605
|
)
|
|
531
606
|
// this can make the stream async-iteration-friendly
|
|
532
607
|
// but not applying it now for perf reasons
|
|
@@ -570,8 +645,10 @@ export class CommonDao<
|
|
|
570
645
|
|
|
571
646
|
await _pipeline([
|
|
572
647
|
this.cfg.db.streamQuery<DBM>(q.select(['id']), opt),
|
|
573
|
-
transformMapSimple<DBM, ID>(objectWithId =>
|
|
574
|
-
|
|
648
|
+
transformMapSimple<DBM, ID>(objectWithId => {
|
|
649
|
+
count++
|
|
650
|
+
return objectWithId.id
|
|
651
|
+
}),
|
|
575
652
|
transformMap<ID, void>(mapper, {
|
|
576
653
|
...opt,
|
|
577
654
|
predicate: _passthroughPredicate,
|
|
@@ -619,8 +696,10 @@ export class CommonDao<
|
|
|
619
696
|
save: async (
|
|
620
697
|
bm: Unsaved<BM>,
|
|
621
698
|
opt: CommonDaoSaveOptions<DBM> = {},
|
|
622
|
-
): Promise<DBSaveBatchOperation> => {
|
|
623
|
-
|
|
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
|
|
624
703
|
|
|
625
704
|
return {
|
|
626
705
|
type: 'saveBatch',
|
|
@@ -636,8 +715,8 @@ export class CommonDao<
|
|
|
636
715
|
bms: Unsaved<BM>[],
|
|
637
716
|
opt: CommonDaoSaveOptions<DBM> = {},
|
|
638
717
|
): Promise<DBSaveBatchOperation | undefined> => {
|
|
639
|
-
if (!bms.length) return
|
|
640
718
|
const rows: DBM[] = (await this.saveBatch(bms, { ...opt, tx: true })) as any
|
|
719
|
+
if (!rows.length) return
|
|
641
720
|
|
|
642
721
|
return {
|
|
643
722
|
type: 'saveBatch',
|
|
@@ -683,9 +762,15 @@ export class CommonDao<
|
|
|
683
762
|
this.requireWriteAccess()
|
|
684
763
|
const idWasGenerated = !bm.id && this.cfg.createId
|
|
685
764
|
this.assignIdCreatedUpdated(bm, opt) // mutates
|
|
686
|
-
|
|
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
|
+
}
|
|
687
771
|
|
|
688
772
|
if (opt.tx) {
|
|
773
|
+
// May return `null`, in which case it'll be skipped
|
|
689
774
|
return dbm as any
|
|
690
775
|
}
|
|
691
776
|
|
|
@@ -698,6 +783,7 @@ export class CommonDao<
|
|
|
698
783
|
const started = this.logSaveStarted(op, bm, table)
|
|
699
784
|
const { excludeFromIndexes } = this.cfg
|
|
700
785
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
786
|
+
|
|
701
787
|
await this.cfg.db.saveBatch(table, [dbm], {
|
|
702
788
|
excludeFromIndexes,
|
|
703
789
|
assignGeneratedIds,
|
|
@@ -764,6 +850,12 @@ export class CommonDao<
|
|
|
764
850
|
const started = this.logSaveStarted(op, row, table)
|
|
765
851
|
const { excludeFromIndexes } = this.cfg
|
|
766
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
|
+
|
|
767
859
|
await this.cfg.db.saveBatch(table, [row], {
|
|
768
860
|
excludeFromIndexes,
|
|
769
861
|
assignGeneratedIds,
|
|
@@ -779,10 +871,17 @@ export class CommonDao<
|
|
|
779
871
|
}
|
|
780
872
|
|
|
781
873
|
async saveBatch(bms: Unsaved<BM>[], opt: CommonDaoSaveOptions<DBM> = {}): Promise<Saved<BM>[]> {
|
|
874
|
+
if (!bms.length) return []
|
|
782
875
|
this.requireWriteAccess()
|
|
783
876
|
const table = opt.table || this.cfg.table
|
|
784
877
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt))
|
|
785
|
-
|
|
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
|
+
}
|
|
786
885
|
|
|
787
886
|
if (opt.tx) {
|
|
788
887
|
return dbms as any
|
|
@@ -803,6 +902,7 @@ export class CommonDao<
|
|
|
803
902
|
const started = this.logSaveStarted(op, bms, table)
|
|
804
903
|
const { excludeFromIndexes } = this.cfg
|
|
805
904
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
905
|
+
|
|
806
906
|
await this.cfg.db.saveBatch(table, dbms, {
|
|
807
907
|
excludeFromIndexes,
|
|
808
908
|
assignGeneratedIds,
|
|
@@ -819,6 +919,7 @@ export class CommonDao<
|
|
|
819
919
|
}
|
|
820
920
|
|
|
821
921
|
async saveBatchAsDBM(dbms: DBM[], opt: CommonDaoSaveOptions<DBM> = {}): Promise<DBM[]> {
|
|
922
|
+
if (!dbms.length) return []
|
|
822
923
|
this.requireWriteAccess()
|
|
823
924
|
const table = opt.table || this.cfg.table
|
|
824
925
|
let rows = dbms
|
|
@@ -841,6 +942,12 @@ export class CommonDao<
|
|
|
841
942
|
const { excludeFromIndexes } = this.cfg
|
|
842
943
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
843
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
|
+
|
|
844
951
|
await this.cfg.db.saveBatch(table, rows, {
|
|
845
952
|
excludeFromIndexes,
|
|
846
953
|
assignGeneratedIds,
|
|
@@ -874,6 +981,7 @@ export class CommonDao<
|
|
|
874
981
|
}
|
|
875
982
|
|
|
876
983
|
async deleteByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<number> {
|
|
984
|
+
if (!ids.length) return 0
|
|
877
985
|
this.requireWriteAccess()
|
|
878
986
|
this.requireObjectMutability(opt)
|
|
879
987
|
const op = `deleteByIds(${ids.join(', ')})`
|
|
@@ -942,6 +1050,7 @@ export class CommonDao<
|
|
|
942
1050
|
}
|
|
943
1051
|
|
|
944
1052
|
async updateByIds(ids: ID[], patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
|
|
1053
|
+
if (!ids.length) return 0
|
|
945
1054
|
return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt)
|
|
946
1055
|
}
|
|
947
1056
|
|
|
@@ -1061,28 +1170,6 @@ export class CommonDao<
|
|
|
1061
1170
|
return bms.map(bm => this.bmToTM(bm, opt))
|
|
1062
1171
|
}
|
|
1063
1172
|
|
|
1064
|
-
tmToBM(tm: undefined, opt?: CommonDaoOptions): undefined
|
|
1065
|
-
tmToBM(tm?: TM, opt?: CommonDaoOptions): BM
|
|
1066
|
-
tmToBM(tm?: TM, opt: CommonDaoOptions = {}): BM | undefined {
|
|
1067
|
-
if (!tm) return
|
|
1068
|
-
|
|
1069
|
-
// optimization: 1 validation is enough
|
|
1070
|
-
// Validate/convert TM
|
|
1071
|
-
// bm gets assigned to the new reference
|
|
1072
|
-
// tm = this.validateAndConvert(tm, this.cfg.tmSchema, DBModelType.TM, opt)
|
|
1073
|
-
|
|
1074
|
-
// TM > BM
|
|
1075
|
-
const bm = this.cfg.hooks!.beforeTMToBM!(tm) as BM
|
|
1076
|
-
|
|
1077
|
-
// Validate/convert BM
|
|
1078
|
-
return this.validateAndConvert<BM>(bm, this.cfg.bmSchema, DBModelType.BM, opt)
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
tmsToBM(tms: TM[], opt: CommonDaoOptions = {}): BM[] {
|
|
1082
|
-
// try/catch?
|
|
1083
|
-
return tms.map(tm => this.tmToBM(tm, opt))
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
1173
|
/**
|
|
1087
1174
|
* Returns *converted value*.
|
|
1088
1175
|
* Validates (unless `skipValidation=true` passed).
|
|
@@ -1210,14 +1297,14 @@ export class CommonDao<
|
|
|
1210
1297
|
this.cfg.logger?.log(`<< ${table}.${op} in ${_since(started)}`)
|
|
1211
1298
|
}
|
|
1212
1299
|
|
|
1213
|
-
protected logStarted(op: string, table: string, force = false):
|
|
1300
|
+
protected logStarted(op: string, table: string, force = false): UnixTimestampMillisNumber {
|
|
1214
1301
|
if (this.cfg.logStarted || force) {
|
|
1215
1302
|
this.cfg.logger?.log(`>> ${table}.${op}`)
|
|
1216
1303
|
}
|
|
1217
1304
|
return Date.now()
|
|
1218
1305
|
}
|
|
1219
1306
|
|
|
1220
|
-
protected logSaveStarted(op: string, items: any, table: string):
|
|
1307
|
+
protected logSaveStarted(op: string, items: any, table: string): UnixTimestampMillisNumber {
|
|
1221
1308
|
if (this.cfg.logStarted) {
|
|
1222
1309
|
const args: any[] = [`>> ${table}.${op}`]
|
|
1223
1310
|
if (Array.isArray(items)) {
|
package/src/db.model.ts
CHANGED