@naturalcycles/db-lib 9.5.0 → 9.7.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.
@@ -2,7 +2,7 @@
2
2
  import { Transform } from 'node:stream';
3
3
  import { AsyncMapper, BaseDBEntity, CommonLogger, JsonSchemaObject, JsonSchemaRootObject, UnixTimestampMillisNumber, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
4
4
  import { AjvSchema, ObjectSchema, ReadableTyped } from '@naturalcycles/nodejs-lib';
5
- import { CommonDBTransactionOptions, DBModelType, DBPatch, DBTransaction, RunQueryResult } from '../db.model';
5
+ import { CommonDBTransactionOptions, DBPatch, DBTransaction, RunQueryResult } from '../db.model';
6
6
  import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
7
7
  import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions } from './common.dao.model';
8
8
  /**
@@ -19,7 +19,6 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
19
19
  getById(id: undefined | null, opt?: CommonDaoOptions): Promise<null>;
20
20
  getById(id?: string | null, opt?: CommonDaoOptions): Promise<BM | null>;
21
21
  getByIdOrEmpty(id: string, part?: Partial<BM>, opt?: CommonDaoOptions): Promise<BM>;
22
- getByIdAsDBMOrEmpty(id: string, part?: Partial<BM>, opt?: CommonDaoOptions): Promise<DBM>;
23
22
  getByIdAsDBM(id: undefined | null, opt?: CommonDaoOptions): Promise<null>;
24
23
  getByIdAsDBM(id?: string | null, opt?: CommonDaoOptions): Promise<DBM | null>;
25
24
  getByIds(ids: string[], opt?: CommonDaoOptions): Promise<BM[]>;
@@ -79,10 +78,6 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
79
78
  * "Returns", just to have a type of "Saved"
80
79
  */
81
80
  assignIdCreatedUpdated<T extends BaseDBEntity>(obj: Partial<T>, opt?: CommonDaoOptions): T;
82
- /**
83
- * Mutates with id, created, updated
84
- */
85
- save(bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
86
81
  /**
87
82
  * 1. Applies the patch
88
83
  * 2. If object is the same after patching - skips saving it
@@ -117,7 +112,11 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
117
112
  * Like patch, but runs all operations within a Transaction.
118
113
  */
119
114
  patchInTransaction(bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM>;
120
- saveAsDBM(dbm: Unsaved<DBM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<DBM>;
115
+ /**
116
+ * Mutates with id, created, updated
117
+ */
118
+ save(bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
119
+ saveAsDBM(dbm: Unsaved<DBM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<DBM>;
121
120
  saveBatch(bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM[]>;
122
121
  saveBatchAsDBM(dbms: Unsaved<DBM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<DBM[]>;
123
122
  /**
@@ -130,7 +129,6 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
130
129
  /**
131
130
  * @returns number of deleted items
132
131
  */
133
- deleteById(id: undefined | null, opt?: CommonDaoOptions): Promise<0>;
134
132
  deleteById(id?: string | null, opt?: CommonDaoOptions): Promise<number>;
135
133
  deleteByIds(ids: string[], opt?: CommonDaoOptions): Promise<number>;
136
134
  /**
@@ -161,7 +159,7 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
161
159
  *
162
160
  * Does NOT mutate the object.
163
161
  */
164
- validateAndConvert<T>(obj: Partial<T>, schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined, modelType?: DBModelType, opt?: CommonDaoOptions): any;
162
+ validateAndConvert<T>(obj: Partial<T>, schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined, opt?: CommonDaoOptions): any;
165
163
  getTableSchema(): Promise<JsonSchemaRootObject<DBM>>;
166
164
  createTable(schema: JsonSchemaObject<DBM>, opt?: CommonDaoCreateOptions): Promise<void>;
167
165
  /**
@@ -194,8 +192,16 @@ export declare class CommonDaoTransaction {
194
192
  rollback(): Promise<void>;
195
193
  getById<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, id?: string | null, opt?: CommonDaoOptions): Promise<BM | null>;
196
194
  getByIds<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, ids: string[], opt?: CommonDaoOptions): Promise<BM[]>;
197
- save<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: Unsaved<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM>;
195
+ save<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
198
196
  saveBatch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM[]>;
197
+ /**
198
+ * DaoTransaction.patch does not load from DB.
199
+ * It assumes the bm was previously loaded in the same Transaction, hence could not be
200
+ * concurrently modified. Hence it's safe to not sync with DB.
201
+ *
202
+ * So, this method is a rather simple convenience "Object.assign and then save".
203
+ */
204
+ patch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(dao: CommonDao<BM, DBM>, bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveOptions<BM, DBM>): Promise<BM>;
199
205
  deleteById(dao: CommonDao<any>, id?: string | null, opt?: CommonDaoOptions): Promise<number>;
200
206
  deleteByIds(dao: CommonDao<any>, ids: string[], opt?: CommonDaoOptions): Promise<number>;
201
207
  }
@@ -4,7 +4,6 @@ exports.CommonDaoTransaction = exports.CommonDao = void 0;
4
4
  const js_lib_1 = require("@naturalcycles/js-lib");
5
5
  const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
6
6
  const cnst_1 = require("../cnst");
7
- const db_model_1 = require("../db.model");
8
7
  const dbQuery_1 = require("../query/dbQuery");
9
8
  const common_dao_model_1 = require("./common.dao.model");
10
9
  const isGAE = !!process.env['GAE_INSTANCE'];
@@ -33,9 +32,6 @@ class CommonDao {
33
32
  hooks: {
34
33
  parseNaturalId: () => ({}),
35
34
  beforeCreate: bm => bm,
36
- beforeDBMValidate: dbm => dbm,
37
- beforeDBMToBM: dbm => dbm,
38
- beforeBMToDBM: bm => bm,
39
35
  anonymize: dbm => dbm,
40
36
  onValidationError: err => err,
41
37
  ...cfg.hooks,
@@ -53,7 +49,7 @@ class CommonDao {
53
49
  const bm = this.cfg.hooks.beforeCreate(part);
54
50
  // First assignIdCreatedUpdated, then validate!
55
51
  this.assignIdCreatedUpdated(bm, opt);
56
- return this.validateAndConvert(bm, this.cfg.bmSchema, db_model_1.DBModelType.BM, opt);
52
+ return this.validateAndConvert(bm, this.cfg.bmSchema, opt);
57
53
  }
58
54
  async getById(id, opt = {}) {
59
55
  if (!id)
@@ -62,10 +58,10 @@ class CommonDao {
62
58
  const table = opt.table || this.cfg.table;
63
59
  const started = this.logStarted(op, table);
64
60
  let dbm = (await (opt.tx || this.cfg.db).getByIds(table, [id]))[0];
65
- if (dbm && !opt.raw && this.cfg.hooks.afterLoad) {
61
+ if (dbm && this.cfg.hooks.afterLoad) {
66
62
  dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
67
63
  }
68
- const bm = opt.raw ? dbm : await this.dbmToBM(dbm, opt);
64
+ const bm = await this.dbmToBM(dbm, opt);
69
65
  this.logResult(started, op, bm, table);
70
66
  return bm || null;
71
67
  }
@@ -75,13 +71,6 @@ class CommonDao {
75
71
  return bm;
76
72
  return this.create({ ...part, id }, opt);
77
73
  }
78
- async getByIdAsDBMOrEmpty(id, part = {}, opt) {
79
- const dbm = await this.getByIdAsDBM(id, opt);
80
- if (dbm)
81
- return dbm;
82
- const bm = this.create({ ...part, id }, opt);
83
- return await this.bmToDBM(bm, opt);
84
- }
85
74
  async getByIdAsDBM(id, opt = {}) {
86
75
  if (!id)
87
76
  return null;
@@ -89,12 +78,10 @@ class CommonDao {
89
78
  const table = opt.table || this.cfg.table;
90
79
  const started = this.logStarted(op, table);
91
80
  let [dbm] = await (opt.tx || this.cfg.db).getByIds(table, [id]);
92
- if (dbm && !opt.raw && this.cfg.hooks.afterLoad) {
81
+ if (dbm && this.cfg.hooks.afterLoad) {
93
82
  dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
94
83
  }
95
- if (!opt.raw) {
96
- dbm = this.anyToDBM(dbm, opt);
97
- }
84
+ dbm = this.anyToDBM(dbm, opt);
98
85
  this.logResult(started, op, dbm, table);
99
86
  return dbm || null;
100
87
  }
@@ -105,10 +92,10 @@ class CommonDao {
105
92
  const table = opt.table || this.cfg.table;
106
93
  const started = this.logStarted(op, table);
107
94
  let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids);
108
- if (!opt.raw && this.cfg.hooks.afterLoad && dbms.length) {
95
+ if (this.cfg.hooks.afterLoad && dbms.length) {
109
96
  dbms = (await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
110
97
  }
111
- const bms = opt.raw ? dbms : await this.dbmsToBM(dbms, opt);
98
+ const bms = await this.dbmsToBM(dbms, opt);
112
99
  this.logResult(started, op, bms, table);
113
100
  return bms;
114
101
  }
@@ -119,7 +106,7 @@ class CommonDao {
119
106
  const table = opt.table || this.cfg.table;
120
107
  const started = this.logStarted(op, table);
121
108
  let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids);
122
- if (!opt.raw && this.cfg.hooks.afterLoad && dbms.length) {
109
+ if (this.cfg.hooks.afterLoad && dbms.length) {
123
110
  dbms = (await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
124
111
  }
125
112
  this.logResult(started, op, dbms, table);
@@ -218,10 +205,10 @@ class CommonDao {
218
205
  const started = this.logStarted(op, q.table);
219
206
  let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
220
207
  const partialQuery = !!q._selectedFieldNames;
221
- if (!opt.raw && this.cfg.hooks.afterLoad && rows.length) {
208
+ if (this.cfg.hooks.afterLoad && rows.length) {
222
209
  rows = (await (0, js_lib_1.pMap)(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
223
210
  }
224
- const bms = partialQuery || opt.raw ? rows : await this.dbmsToBM(rows, opt);
211
+ const bms = partialQuery ? rows : await this.dbmsToBM(rows, opt);
225
212
  this.logResult(started, op, bms, q.table);
226
213
  return {
227
214
  rows: bms,
@@ -237,11 +224,11 @@ class CommonDao {
237
224
  const op = `runQueryAsDBM(${q.pretty()})`;
238
225
  const started = this.logStarted(op, q.table);
239
226
  let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
240
- if (!opt.raw && this.cfg.hooks.afterLoad && rows.length) {
227
+ if (this.cfg.hooks.afterLoad && rows.length) {
241
228
  rows = (await (0, js_lib_1.pMap)(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
242
229
  }
243
230
  const partialQuery = !!q._selectedFieldNames;
244
- const dbms = partialQuery || opt.raw ? rows : this.anyToDBMs(rows, opt);
231
+ const dbms = partialQuery ? rows : this.anyToDBMs(rows, opt);
245
232
  this.logResult(started, op, dbms, q.table);
246
233
  return { rows: dbms, ...queryResult };
247
234
  }
@@ -268,7 +255,7 @@ class CommonDao {
268
255
  this.cfg.db.streamQuery(q, opt),
269
256
  (0, nodejs_lib_1.transformMap)(async (dbm) => {
270
257
  count++;
271
- if (partialQuery || opt.raw)
258
+ if (partialQuery)
272
259
  return dbm;
273
260
  if (this.cfg.hooks.afterLoad) {
274
261
  dbm = (await this.cfg.hooks.afterLoad(dbm));
@@ -307,7 +294,7 @@ class CommonDao {
307
294
  this.cfg.db.streamQuery(q, opt),
308
295
  (0, nodejs_lib_1.transformMap)(async (dbm) => {
309
296
  count++;
310
- if (partialQuery || opt.raw)
297
+ if (partialQuery)
311
298
  return dbm;
312
299
  if (this.cfg.hooks.afterLoad) {
313
300
  dbm = (await this.cfg.hooks.afterLoad(dbm));
@@ -343,7 +330,7 @@ class CommonDao {
343
330
  opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
344
331
  const partialQuery = !!q._selectedFieldNames;
345
332
  const stream = this.cfg.db.streamQuery(q, opt);
346
- if (partialQuery || opt.raw)
333
+ if (partialQuery)
347
334
  return stream;
348
335
  return stream
349
336
  .on('error', err => stream.emit('error', err))
@@ -374,7 +361,7 @@ class CommonDao {
374
361
  opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
375
362
  const stream = this.cfg.db.streamQuery(q, opt);
376
363
  const partialQuery = !!q._selectedFieldNames;
377
- if (partialQuery || opt.raw)
364
+ if (partialQuery)
378
365
  return stream;
379
366
  return (stream
380
367
  // optimization: 1 validation is enough
@@ -457,45 +444,6 @@ class CommonDao {
457
444
  return obj;
458
445
  }
459
446
  // SAVE
460
- /**
461
- * Mutates with id, created, updated
462
- */
463
- async save(bm, opt = {}) {
464
- this.requireWriteAccess();
465
- if (opt.skipIfEquals && (0, js_lib_1._deepJsonEquals)(bm, opt.skipIfEquals)) {
466
- // Skipping the save operation
467
- return bm;
468
- }
469
- const idWasGenerated = !bm.id && this.cfg.generateId;
470
- this.assignIdCreatedUpdated(bm, opt); // mutates
471
- (0, js_lib_1._typeCast)(bm);
472
- let dbm = await this.bmToDBM(bm, opt);
473
- if (this.cfg.hooks.beforeSave) {
474
- dbm = (await this.cfg.hooks.beforeSave(dbm));
475
- if (dbm === null)
476
- return bm;
477
- }
478
- const table = opt.table || this.cfg.table;
479
- if (opt.ensureUniqueId && idWasGenerated)
480
- await this.ensureUniqueId(table, dbm);
481
- if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
482
- opt = { ...opt, saveMethod: 'insert' };
483
- }
484
- const op = `save(${dbm.id})`;
485
- const started = this.logSaveStarted(op, bm, table);
486
- const { excludeFromIndexes } = this.cfg;
487
- const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
488
- await (opt.tx || this.cfg.db).saveBatch(table, [dbm], {
489
- excludeFromIndexes,
490
- assignGeneratedIds,
491
- ...opt,
492
- });
493
- if (assignGeneratedIds) {
494
- bm.id = dbm.id;
495
- }
496
- this.logSaveResult(started, op, table);
497
- return bm;
498
- }
499
447
  /**
500
448
  * 1. Applies the patch
501
449
  * 2. If object is the same after patching - skips saving it
@@ -591,19 +539,55 @@ class CommonDao {
591
539
  return await this.patch(bm, patch, { ...opt, tx: daoTx.tx });
592
540
  });
593
541
  }
542
+ /**
543
+ * Mutates with id, created, updated
544
+ */
545
+ async save(bm, opt = {}) {
546
+ this.requireWriteAccess();
547
+ if (opt.skipIfEquals && (0, js_lib_1._deepJsonEquals)(bm, opt.skipIfEquals)) {
548
+ // Skipping the save operation
549
+ return bm;
550
+ }
551
+ const idWasGenerated = !bm.id && this.cfg.generateId;
552
+ this.assignIdCreatedUpdated(bm, opt); // mutates
553
+ (0, js_lib_1._typeCast)(bm);
554
+ let dbm = await this.bmToDBM(bm, opt); // validates BM
555
+ if (this.cfg.hooks.beforeSave) {
556
+ dbm = (await this.cfg.hooks.beforeSave(dbm));
557
+ if (dbm === null)
558
+ return bm;
559
+ }
560
+ const table = opt.table || this.cfg.table;
561
+ if (opt.ensureUniqueId && idWasGenerated)
562
+ await this.ensureUniqueId(table, dbm);
563
+ if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
564
+ opt = { ...opt, saveMethod: 'insert' };
565
+ }
566
+ const op = `save(${dbm.id})`;
567
+ const started = this.logSaveStarted(op, bm, table);
568
+ const { excludeFromIndexes } = this.cfg;
569
+ const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
570
+ await (opt.tx || this.cfg.db).saveBatch(table, [dbm], {
571
+ excludeFromIndexes,
572
+ assignGeneratedIds,
573
+ ...opt,
574
+ });
575
+ if (assignGeneratedIds) {
576
+ bm.id = dbm.id;
577
+ }
578
+ this.logSaveResult(started, op, table);
579
+ return bm;
580
+ }
594
581
  async saveAsDBM(dbm, opt = {}) {
595
582
  this.requireWriteAccess();
596
583
  const table = opt.table || this.cfg.table;
597
584
  // assigning id in case it misses the id
598
585
  // will override/set `updated` field, unless opts.preserveUpdated is set
599
- let row = dbm;
600
- if (!opt.raw) {
601
- const idWasGenerated = !dbm.id && this.cfg.generateId;
602
- this.assignIdCreatedUpdated(dbm, opt); // mutates
603
- row = this.anyToDBM(dbm, opt);
604
- if (opt.ensureUniqueId && idWasGenerated)
605
- await this.ensureUniqueId(table, row);
606
- }
586
+ const idWasGenerated = !dbm.id && this.cfg.generateId;
587
+ this.assignIdCreatedUpdated(dbm, opt); // mutates
588
+ let row = this.anyToDBM(dbm, opt);
589
+ if (opt.ensureUniqueId && idWasGenerated)
590
+ await this.ensureUniqueId(table, row);
607
591
  if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
608
592
  opt = { ...opt, saveMethod: 'insert' };
609
593
  }
@@ -665,13 +649,10 @@ class CommonDao {
665
649
  return [];
666
650
  this.requireWriteAccess();
667
651
  const table = opt.table || this.cfg.table;
668
- let rows = dbms;
669
- if (!opt.raw) {
670
- dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt)); // mutates
671
- rows = this.anyToDBMs(dbms, opt);
672
- if (opt.ensureUniqueId)
673
- throw new js_lib_1.AppError('ensureUniqueId is not supported in saveBatch');
674
- }
652
+ dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt)); // mutates
653
+ let rows = this.anyToDBMs(dbms, opt);
654
+ if (opt.ensureUniqueId)
655
+ throw new js_lib_1.AppError('ensureUniqueId is not supported in saveBatch');
675
656
  if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
676
657
  opt = { ...opt, saveMethod: 'insert' };
677
658
  }
@@ -748,17 +729,14 @@ class CommonDao {
748
729
  (0, nodejs_lib_1.writableVoid)(),
749
730
  ];
750
731
  }
732
+ // DELETE
733
+ /**
734
+ * @returns number of deleted items
735
+ */
751
736
  async deleteById(id, opt = {}) {
752
737
  if (!id)
753
738
  return 0;
754
- this.requireWriteAccess();
755
- this.requireObjectMutability(opt);
756
- const op = `deleteById(${id})`;
757
- const table = opt.table || this.cfg.table;
758
- const started = this.logStarted(op, table);
759
- const count = await this.cfg.db.deleteByIds(table, [id], opt);
760
- this.logSaveResult(started, op, table);
761
- return count;
739
+ return await this.deleteByIds([id], opt);
762
740
  }
763
741
  async deleteByIds(ids, opt = {}) {
764
742
  if (!ids.length)
@@ -842,9 +820,15 @@ class CommonDao {
842
820
  dbm = this.cfg.hooks.anonymize(dbm);
843
821
  }
844
822
  // DBM > BM
845
- const bm = await this.cfg.hooks.beforeDBMToBM(dbm);
823
+ let bm;
824
+ if (this.cfg.hooks.beforeDBMToBM) {
825
+ bm = await this.cfg.hooks.beforeDBMToBM(dbm);
826
+ }
827
+ else {
828
+ bm = dbm;
829
+ }
846
830
  // Validate/convert BM
847
- return this.validateAndConvert(bm, this.cfg.bmSchema, db_model_1.DBModelType.BM, opt);
831
+ return this.validateAndConvert(bm, this.cfg.bmSchema, opt);
848
832
  }
849
833
  async dbmsToBM(dbms, opt = {}) {
850
834
  return await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.dbmToBM(dbm, opt));
@@ -852,16 +836,21 @@ class CommonDao {
852
836
  async bmToDBM(bm, opt) {
853
837
  if (bm === undefined)
854
838
  return;
855
- // optimization: no need to run the BM validation, since DBM will be validated anyway
856
- // Validate/convert BM
857
- // bm gets assigned to the new reference
858
- // bm = this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
859
839
  // should not do it on load, but only on save!
860
840
  // this.assignIdCreatedUpdated(bm, opt)
841
+ // bm gets assigned to the new reference
842
+ bm = this.validateAndConvert(bm, this.cfg.bmSchema, opt);
861
843
  // BM > DBM
862
- const dbm = { ...(await this.cfg.hooks.beforeBMToDBM(bm)) };
844
+ let dbm;
845
+ if (this.cfg.hooks.beforeBMToDBM) {
846
+ dbm = { ...(await this.cfg.hooks.beforeBMToDBM(bm)) };
847
+ }
848
+ else {
849
+ dbm = bm;
850
+ }
863
851
  // Validate/convert DBM
864
- return this.validateAndConvert(dbm, this.cfg.dbmSchema, db_model_1.DBModelType.DBM, opt);
852
+ // return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
853
+ return dbm;
865
854
  }
866
855
  async bmsToDBM(bms, opt = {}) {
867
856
  // try/catch?
@@ -873,11 +862,14 @@ class CommonDao {
873
862
  // this shouldn't be happening on load! but should on save!
874
863
  // this.assignIdCreatedUpdated(dbm, opt)
875
864
  dbm = { ...dbm, ...this.cfg.hooks.parseNaturalId(dbm.id) };
865
+ // todo: is this the right place?
866
+ // todo: is anyToDBM even needed?
876
867
  if (opt.anonymize) {
877
868
  dbm = this.cfg.hooks.anonymize(dbm);
878
869
  }
879
870
  // Validate/convert DBM
880
- return this.validateAndConvert(dbm, this.cfg.dbmSchema, db_model_1.DBModelType.DBM, opt);
871
+ // return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
872
+ return dbm;
881
873
  }
882
874
  anyToDBMs(entities, opt = {}) {
883
875
  return entities.map(entity => this.anyToDBM(entity, opt));
@@ -888,10 +880,7 @@ class CommonDao {
888
880
  *
889
881
  * Does NOT mutate the object.
890
882
  */
891
- validateAndConvert(obj, schema, modelType, opt = {}) {
892
- // `raw` option completely bypasses any processing
893
- if (opt.raw)
894
- return obj;
883
+ validateAndConvert(obj, schema, opt = {}) {
895
884
  // Kirill 2021-10-18: I realized that there's little reason to keep removing null values
896
885
  // So, from now on we'll preserve them
897
886
  // "undefined" values, I believe, are/were not saved to/from DB anyway (due to e.g JSON.stringify removing them)
@@ -901,23 +890,14 @@ class CommonDao {
901
890
  // obj = _filterNullishValues(obj as any)
902
891
  // We still filter `undefined` values here, because `beforeDBMToBM` can return undefined values
903
892
  // and they can be annoying with snapshot tests
904
- if (this.cfg.filterNullishValues) {
905
- obj = (0, js_lib_1._filterNullishValues)(obj);
906
- }
907
- else {
908
- obj = (0, js_lib_1._filterUndefinedValues)(obj);
909
- }
910
- // Pre-validation hooks
911
- if (modelType === db_model_1.DBModelType.DBM) {
912
- obj = this.cfg.hooks.beforeDBMValidate(obj);
913
- }
893
+ obj = (0, js_lib_1._filterUndefinedValues)(obj);
914
894
  // Return as is if no schema is passed or if `skipConversion` is set
915
895
  if (!schema || opt.skipConversion) {
916
896
  return obj;
917
897
  }
918
898
  // This will Convert and Validate
919
899
  const table = opt.table || this.cfg.table;
920
- const objectName = table + (modelType || '');
900
+ const objectName = table;
921
901
  let error;
922
902
  let convertedValue;
923
903
  if (schema instanceof js_lib_1.ZodSchema) {
@@ -1070,11 +1050,23 @@ class CommonDaoTransaction {
1070
1050
  // }
1071
1051
  // }
1072
1052
  async save(dao, bm, opt) {
1073
- return (await this.saveBatch(dao, [bm], opt))[0];
1053
+ return await dao.save(bm, { ...opt, tx: this.tx });
1074
1054
  }
1075
1055
  async saveBatch(dao, bms, opt) {
1076
1056
  return await dao.saveBatch(bms, { ...opt, tx: this.tx });
1077
1057
  }
1058
+ /**
1059
+ * DaoTransaction.patch does not load from DB.
1060
+ * It assumes the bm was previously loaded in the same Transaction, hence could not be
1061
+ * concurrently modified. Hence it's safe to not sync with DB.
1062
+ *
1063
+ * So, this method is a rather simple convenience "Object.assign and then save".
1064
+ */
1065
+ async patch(dao, bm, patch, opt) {
1066
+ const skipIfEquals = (0, js_lib_1._deepCopy)(bm);
1067
+ Object.assign(bm, patch);
1068
+ return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx });
1069
+ }
1078
1070
  async deleteById(dao, id, opt) {
1079
1071
  if (!id)
1080
1072
  return 0;
@@ -28,16 +28,6 @@ export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntit
28
28
  * - patch, patchAsDBM
29
29
  */
30
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
- */
40
- beforeDBMValidate: (dbm: Partial<DBM>) => Partial<DBM>;
41
31
  beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>;
42
32
  beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>;
43
33
  /**
@@ -109,7 +99,6 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
109
99
  /**
110
100
  * Joi, AjvSchema or ZodSchema is supported.
111
101
  */
112
- dbmSchema?: ObjectSchema<DBM> | AjvSchema<DBM> | ZodSchema<DBM>;
113
102
  bmSchema?: ObjectSchema<BM> | AjvSchema<BM> | ZodSchema<BM>;
114
103
  excludeFromIndexes?: (keyof DBM)[];
115
104
  /**
@@ -161,16 +150,6 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
161
150
  * Set to false to disable `updated` field management.
162
151
  */
163
152
  useUpdatedProperty?: boolean;
164
- /**
165
- * Default is false.
166
- * If true - will run `_filterNullishValues` inside `validateAndConvert` function
167
- * (instead of `_filterUndefinedValues`).
168
- * This was the old db-lib behavior.
169
- * This option allows to keep backwards-compatible behavior.
170
- *
171
- * @deprecated
172
- */
173
- filterNullishValues?: boolean;
174
153
  /**
175
154
  * Defaults to false.
176
155
  * If true - run patch operations (patch, patchById) in a Transaction.
@@ -198,15 +177,6 @@ export interface CommonDaoOptions extends CommonDBOptions {
198
177
  * @default false
199
178
  */
200
179
  skipConversion?: boolean;
201
- /**
202
- * If true - will SKIP ANY transformation/processing, will return DB objects as they are. Will also skip created/updated/id
203
- * generation.
204
- *
205
- * Useful for performance/streaming/pipelines.
206
- *
207
- * @default false
208
- */
209
- raw?: boolean;
210
180
  /**
211
181
  * @default false
212
182
  */
@@ -13,7 +13,6 @@ function runCommonDaoTest(db, quirks = {}) {
13
13
  const dao = new common_dao_1.CommonDao({
14
14
  table: test_model_1.TEST_TABLE,
15
15
  db,
16
- dbmSchema: test_model_1.testItemDBMSchema,
17
16
  bmSchema: test_model_1.testItemBMSchema,
18
17
  logStarted: true,
19
18
  logLevel: __1.CommonDaoLogLevel.DATA_FULL,
@@ -32,7 +31,7 @@ function runCommonDaoTest(db, quirks = {}) {
32
31
  // CREATE TABLE, DROP
33
32
  if (support.createTable) {
34
33
  test('createTable, dropIfExists=true', async () => {
35
- await dao.createTable(test_model_1.testItemDBMJsonSchema, { dropIfExists: true });
34
+ await dao.createTable(test_model_1.testItemBMJsonSchema, { dropIfExists: true });
36
35
  });
37
36
  }
38
37
  if (support.queries) {
@@ -20,7 +20,7 @@ function runCommonDBTest(db, quirks = {}) {
20
20
  // CREATE TABLE, DROP
21
21
  if (support.createTable) {
22
22
  test('createTable, dropIfExists=true', async () => {
23
- await db.createTable(test_model_1.TEST_TABLE, test_model_1.testItemDBMJsonSchema, { dropIfExists: true });
23
+ await db.createTable(test_model_1.TEST_TABLE, test_model_1.testItemBMJsonSchema, { dropIfExists: true });
24
24
  });
25
25
  }
26
26
  if (support.queries) {
@@ -1,6 +1,6 @@
1
1
  import { runCommonDaoTest } from './daoTest';
2
2
  import { CommonDBImplementationQuirks, runCommonDBTest } from './dbTest';
3
3
  import { runCommonKeyValueDBTest } from './keyValueDBTest';
4
- import { createTestItemBM, createTestItemDBM, createTestItemsBM, createTestItemsDBM, TestItemBM, testItemBMJsonSchema, testItemBMSchema, TestItemDBM, testItemDBMJsonSchema, testItemDBMSchema, TestItemTM, testItemTMSchema, TEST_TABLE } from './test.model';
4
+ import { createTestItemBM, createTestItemDBM, createTestItemsBM, createTestItemsDBM, TestItemBM, testItemBMJsonSchema, testItemBMSchema, TestItemDBM, TestItemTM, testItemTMSchema, TEST_TABLE } from './test.model';
5
5
  export type { TestItemDBM, TestItemBM, TestItemTM, CommonDBImplementationQuirks };
6
- export { TEST_TABLE, createTestItemDBM, createTestItemBM, createTestItemsDBM, createTestItemsBM, testItemDBMSchema, testItemBMSchema, testItemTMSchema, testItemBMJsonSchema, testItemDBMJsonSchema, runCommonDBTest, runCommonDaoTest, runCommonKeyValueDBTest, };
6
+ export { TEST_TABLE, createTestItemDBM, createTestItemBM, createTestItemsDBM, createTestItemsBM, testItemBMSchema, testItemTMSchema, testItemBMJsonSchema, runCommonDBTest, runCommonDaoTest, runCommonKeyValueDBTest, };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runCommonKeyValueDBTest = exports.runCommonDaoTest = exports.runCommonDBTest = exports.testItemDBMJsonSchema = exports.testItemBMJsonSchema = exports.testItemTMSchema = exports.testItemBMSchema = exports.testItemDBMSchema = exports.createTestItemsBM = exports.createTestItemsDBM = exports.createTestItemBM = exports.createTestItemDBM = exports.TEST_TABLE = void 0;
3
+ exports.runCommonKeyValueDBTest = exports.runCommonDaoTest = exports.runCommonDBTest = exports.testItemBMJsonSchema = exports.testItemTMSchema = exports.testItemBMSchema = exports.createTestItemsBM = exports.createTestItemsDBM = exports.createTestItemBM = exports.createTestItemDBM = exports.TEST_TABLE = void 0;
4
4
  const daoTest_1 = require("./daoTest");
5
5
  Object.defineProperty(exports, "runCommonDaoTest", { enumerable: true, get: function () { return daoTest_1.runCommonDaoTest; } });
6
6
  const dbTest_1 = require("./dbTest");
@@ -14,7 +14,5 @@ Object.defineProperty(exports, "createTestItemsBM", { enumerable: true, get: fun
14
14
  Object.defineProperty(exports, "createTestItemsDBM", { enumerable: true, get: function () { return test_model_1.createTestItemsDBM; } });
15
15
  Object.defineProperty(exports, "testItemBMJsonSchema", { enumerable: true, get: function () { return test_model_1.testItemBMJsonSchema; } });
16
16
  Object.defineProperty(exports, "testItemBMSchema", { enumerable: true, get: function () { return test_model_1.testItemBMSchema; } });
17
- Object.defineProperty(exports, "testItemDBMJsonSchema", { enumerable: true, get: function () { return test_model_1.testItemDBMJsonSchema; } });
18
- Object.defineProperty(exports, "testItemDBMSchema", { enumerable: true, get: function () { return test_model_1.testItemDBMSchema; } });
19
17
  Object.defineProperty(exports, "testItemTMSchema", { enumerable: true, get: function () { return test_model_1.testItemTMSchema; } });
20
18
  Object.defineProperty(exports, "TEST_TABLE", { enumerable: true, get: function () { return test_model_1.TEST_TABLE; } });
@@ -15,10 +15,8 @@ export interface TestItemTM {
15
15
  even?: boolean;
16
16
  }
17
17
  export declare const testItemBMSchema: import("joi").ObjectSchema<TestItemBM>;
18
- export declare const testItemDBMSchema: import("joi").ObjectSchema<TestItemDBM>;
19
18
  export declare const testItemTMSchema: import("joi").ObjectSchema<TestItemTM>;
20
19
  export declare const testItemBMJsonSchema: JsonSchemaObject<TestItemBM>;
21
- export declare const testItemDBMJsonSchema: JsonSchemaObject<TestItemDBM>;
22
20
  export declare function createTestItemDBM(num?: number): TestItemDBM;
23
21
  export declare function createTestItemBM(num?: number): TestItemBM;
24
22
  export declare function createTestItemsDBM(count?: number): TestItemDBM[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createTestItemsBM = exports.createTestItemsDBM = exports.createTestItemBM = exports.createTestItemDBM = exports.testItemDBMJsonSchema = exports.testItemBMJsonSchema = exports.testItemTMSchema = exports.testItemDBMSchema = exports.testItemBMSchema = exports.TEST_TABLE = void 0;
3
+ exports.createTestItemsBM = exports.createTestItemsDBM = exports.createTestItemBM = exports.createTestItemDBM = exports.testItemBMJsonSchema = exports.testItemTMSchema = exports.testItemBMSchema = exports.TEST_TABLE = void 0;
4
4
  const js_lib_1 = require("@naturalcycles/js-lib");
5
5
  const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
6
6
  const MOCK_TS_2018_06_21 = 1529539200;
@@ -12,13 +12,6 @@ exports.testItemBMSchema = (0, nodejs_lib_1.objectSchema)({
12
12
  even: nodejs_lib_1.booleanSchema.optional(),
13
13
  b1: nodejs_lib_1.binarySchema.optional(),
14
14
  }).concat(nodejs_lib_1.baseDBEntitySchema);
15
- exports.testItemDBMSchema = (0, nodejs_lib_1.objectSchema)({
16
- k1: nodejs_lib_1.stringSchema,
17
- k2: nodejs_lib_1.stringSchema.allow(null).optional(),
18
- k3: nodejs_lib_1.numberSchema.optional(),
19
- even: nodejs_lib_1.booleanSchema.optional(),
20
- b1: nodejs_lib_1.binarySchema.optional(),
21
- }).concat(nodejs_lib_1.baseDBEntitySchema);
22
15
  exports.testItemTMSchema = (0, nodejs_lib_1.objectSchema)({
23
16
  k1: nodejs_lib_1.stringSchema,
24
17
  even: nodejs_lib_1.booleanSchema.optional(),
@@ -37,19 +30,6 @@ exports.testItemBMJsonSchema = js_lib_1.jsonSchema
37
30
  })
38
31
  .baseDBEntity()
39
32
  .build();
40
- exports.testItemDBMJsonSchema = js_lib_1.jsonSchema
41
- .rootObject({
42
- // todo: figure out how to not copy-paste these 3 fields
43
- id: js_lib_1.jsonSchema.string(),
44
- created: js_lib_1.jsonSchema.unixTimestamp(),
45
- updated: js_lib_1.jsonSchema.unixTimestamp(),
46
- k1: js_lib_1.jsonSchema.string(),
47
- k2: js_lib_1.jsonSchema.string().optional(),
48
- k3: js_lib_1.jsonSchema.number().optional(),
49
- even: js_lib_1.jsonSchema.boolean().optional(),
50
- b1: js_lib_1.jsonSchema.buffer().optional(),
51
- })
52
- .build();
53
33
  function createTestItemDBM(num = 1) {
54
34
  return {
55
35
  id: `id${num}`,