@naturalcycles/db-lib 10.19.0 → 10.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commondao/common.dao.d.ts +36 -63
- package/dist/commondao/common.dao.js +138 -432
- package/dist/commondao/common.dao.model.d.ts +0 -13
- package/dist/commondao/commonDaoTransaction.d.ts +38 -0
- package/dist/commondao/commonDaoTransaction.js +78 -0
- package/dist/testing/commonDaoTest.js +0 -3
- package/package.json +1 -1
- package/src/commondao/common.dao.model.ts +4 -4
- package/src/commondao/common.dao.ts +188 -519
- package/src/commondao/commonDaoTransaction.ts +124 -0
- package/src/testing/commonDaoTest.ts +0 -3
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { _isTruthy } from '@naturalcycles/js-lib';
|
|
2
2
|
import { _uniqBy } from '@naturalcycles/js-lib/array/array.util.js';
|
|
3
3
|
import { localTime } from '@naturalcycles/js-lib/datetime/localTime.js';
|
|
4
|
-
import {
|
|
5
|
-
import { _assert, AppError, ErrorMode } from '@naturalcycles/js-lib/error';
|
|
4
|
+
import { _assert, ErrorMode } from '@naturalcycles/js-lib/error';
|
|
6
5
|
import { _deepJsonEquals } from '@naturalcycles/js-lib/object/deepEquals.js';
|
|
7
|
-
import {
|
|
6
|
+
import { _filterUndefinedValues, _objectAssignExact, } from '@naturalcycles/js-lib/object/object.util.js';
|
|
8
7
|
import { pMap } from '@naturalcycles/js-lib/promise/pMap.js';
|
|
9
|
-
import { _truncate } from '@naturalcycles/js-lib/string/string.util.js';
|
|
10
8
|
import { _stringMapEntries, _stringMapValues, } from '@naturalcycles/js-lib/types';
|
|
11
9
|
import { _passthroughPredicate, _typeCast, SKIP } from '@naturalcycles/js-lib/types';
|
|
12
10
|
import { stringId } from '@naturalcycles/nodejs-lib';
|
|
13
|
-
import { transformFlatten } from '@naturalcycles/nodejs-lib/stream';
|
|
11
|
+
import { transformFlatten, transformMapSync, } from '@naturalcycles/nodejs-lib/stream';
|
|
14
12
|
import { _pipeline, transformChunk, transformLogProgress, transformMap, transformNoOp, writableVoid, } from '@naturalcycles/nodejs-lib/stream';
|
|
15
13
|
import { DBLibError } from '../cnst.js';
|
|
16
14
|
import { RunnableDBQuery } from '../query/dbQuery.js';
|
|
17
|
-
import {
|
|
15
|
+
import { CommonDaoTransaction } from './commonDaoTransaction.js';
|
|
18
16
|
/**
|
|
19
17
|
* Lowest common denominator API between supported Databases.
|
|
20
18
|
*
|
|
@@ -27,7 +25,6 @@ export class CommonDao {
|
|
|
27
25
|
constructor(cfg) {
|
|
28
26
|
this.cfg = cfg;
|
|
29
27
|
this.cfg = {
|
|
30
|
-
logLevel: CommonDaoLogLevel.NONE,
|
|
31
28
|
generateId: true,
|
|
32
29
|
assignGeneratedIds: false,
|
|
33
30
|
useCreatedProperty: true,
|
|
@@ -59,22 +56,13 @@ export class CommonDao {
|
|
|
59
56
|
return this.validateAndConvert(bm, undefined, opt);
|
|
60
57
|
}
|
|
61
58
|
// GET
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const table = opt.table || this.cfg.table;
|
|
70
|
-
const started = this.logStarted(op, table);
|
|
71
|
-
let dbm = (await (opt.tx || this.cfg.db).getByIds(table, [id], opt))[0];
|
|
72
|
-
if (dbm && this.cfg.hooks.afterLoad) {
|
|
73
|
-
dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
|
|
74
|
-
}
|
|
75
|
-
const bm = await this.dbmToBM(dbm, opt);
|
|
76
|
-
this.logResult(started, op, bm, table);
|
|
77
|
-
return bm || null;
|
|
59
|
+
async requireById(id, opt = {}) {
|
|
60
|
+
const bm = await this.getById(id, opt);
|
|
61
|
+
return this.ensureRequired(bm, id, opt);
|
|
62
|
+
}
|
|
63
|
+
async requireByIdAsDBM(id, opt = {}) {
|
|
64
|
+
const dbm = await this.getByIdAsDBM(id, opt);
|
|
65
|
+
return this.ensureRequired(dbm, id, opt);
|
|
78
66
|
}
|
|
79
67
|
async getByIdOrEmpty(id, part = {}, opt) {
|
|
80
68
|
const bm = await this.getById(id, opt);
|
|
@@ -82,99 +70,32 @@ export class CommonDao {
|
|
|
82
70
|
return bm;
|
|
83
71
|
return this.create({ ...part, id }, opt);
|
|
84
72
|
}
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
async getById(id, opt = {}) {
|
|
74
|
+
if (!id)
|
|
75
|
+
return null;
|
|
76
|
+
const [dbm] = await this.loadByIds([id], opt);
|
|
77
|
+
return await this.dbmToBM(dbm, opt);
|
|
78
|
+
}
|
|
87
79
|
async getByIdAsDBM(id, opt = {}) {
|
|
88
80
|
if (!id)
|
|
89
81
|
return null;
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
const started = this.logStarted(op, table);
|
|
93
|
-
let [dbm] = await (opt.tx || this.cfg.db).getByIds(table, [id], opt);
|
|
94
|
-
if (dbm && this.cfg.hooks.afterLoad) {
|
|
95
|
-
dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
|
|
96
|
-
}
|
|
97
|
-
dbm = this.anyToDBM(dbm, opt);
|
|
98
|
-
this.logResult(started, op, dbm, table);
|
|
99
|
-
return dbm || null;
|
|
82
|
+
const [row] = await this.loadByIds([id], opt);
|
|
83
|
+
return this.anyToDBM(row, opt) || null;
|
|
100
84
|
}
|
|
101
85
|
async getByIds(ids, opt = {}) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`;
|
|
105
|
-
const table = opt.table || this.cfg.table;
|
|
106
|
-
const started = this.logStarted(op, table);
|
|
107
|
-
let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids, opt);
|
|
108
|
-
if (this.cfg.hooks.afterLoad && dbms.length) {
|
|
109
|
-
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
110
|
-
}
|
|
111
|
-
const bms = await this.dbmsToBM(dbms, opt);
|
|
112
|
-
this.logResult(started, op, bms, table);
|
|
113
|
-
return bms;
|
|
86
|
+
const dbms = await this.loadByIds(ids, opt);
|
|
87
|
+
return await this.dbmsToBM(dbms, opt);
|
|
114
88
|
}
|
|
115
89
|
async getByIdsAsDBM(ids, opt = {}) {
|
|
90
|
+
const rows = await this.loadByIds(ids, opt);
|
|
91
|
+
return this.anyToDBMs(rows);
|
|
92
|
+
}
|
|
93
|
+
// DRY private method
|
|
94
|
+
async loadByIds(ids, opt = {}) {
|
|
116
95
|
if (!ids.length)
|
|
117
96
|
return [];
|
|
118
|
-
const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`;
|
|
119
|
-
const table = opt.table || this.cfg.table;
|
|
120
|
-
const started = this.logStarted(op, table);
|
|
121
|
-
let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids, opt);
|
|
122
|
-
if (this.cfg.hooks.afterLoad && dbms.length) {
|
|
123
|
-
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
124
|
-
}
|
|
125
|
-
this.logResult(started, op, dbms, table);
|
|
126
|
-
return dbms;
|
|
127
|
-
}
|
|
128
|
-
async requireById(id, opt = {}) {
|
|
129
|
-
const r = await this.getById(id, opt);
|
|
130
|
-
if (!r) {
|
|
131
|
-
this.throwRequiredError(id, opt);
|
|
132
|
-
}
|
|
133
|
-
return r;
|
|
134
|
-
}
|
|
135
|
-
async requireByIdAsDBM(id, opt = {}) {
|
|
136
|
-
const r = await this.getByIdAsDBM(id, opt);
|
|
137
|
-
if (!r) {
|
|
138
|
-
this.throwRequiredError(id, opt);
|
|
139
|
-
}
|
|
140
|
-
return r;
|
|
141
|
-
}
|
|
142
|
-
throwRequiredError(id, opt) {
|
|
143
97
|
const table = opt.table || this.cfg.table;
|
|
144
|
-
|
|
145
|
-
table,
|
|
146
|
-
id,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Throws if readOnly is true
|
|
151
|
-
*/
|
|
152
|
-
requireWriteAccess() {
|
|
153
|
-
if (this.cfg.readOnly) {
|
|
154
|
-
throw new AppError(DBLibError.DAO_IS_READ_ONLY, {
|
|
155
|
-
table: this.cfg.table,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Throws if readOnly is true
|
|
161
|
-
*/
|
|
162
|
-
requireObjectMutability(opt) {
|
|
163
|
-
if (this.cfg.immutable && !opt.allowMutability) {
|
|
164
|
-
throw new AppError(DBLibError.OBJECT_IS_IMMUTABLE, {
|
|
165
|
-
table: this.cfg.table,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
async ensureUniqueId(table, dbm) {
|
|
170
|
-
// todo: retry N times
|
|
171
|
-
const existing = await this.cfg.db.getByIds(table, [dbm.id]);
|
|
172
|
-
if (existing.length) {
|
|
173
|
-
throw new AppError(DBLibError.NON_UNIQUE_ID, {
|
|
174
|
-
table,
|
|
175
|
-
ids: existing.map(i => i.id),
|
|
176
|
-
});
|
|
177
|
-
}
|
|
98
|
+
return await (opt.tx || this.cfg.db).getByIds(table, ids, opt);
|
|
178
99
|
}
|
|
179
100
|
async getBy(by, value, limit = 0, opt) {
|
|
180
101
|
return await this.query().filterEq(by, value).limit(limit).runQuery(opt);
|
|
@@ -215,15 +136,9 @@ export class CommonDao {
|
|
|
215
136
|
async runQueryExtended(q, opt = {}) {
|
|
216
137
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
217
138
|
q.table = opt.table || q.table;
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
const partialQuery = !!q._selectedFieldNames;
|
|
222
|
-
if (this.cfg.hooks.afterLoad && rows.length) {
|
|
223
|
-
rows = (await pMap(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
224
|
-
}
|
|
225
|
-
const bms = partialQuery ? rows : await this.dbmsToBM(rows, opt);
|
|
226
|
-
this.logResult(started, op, bms, q.table);
|
|
139
|
+
const { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
140
|
+
const isPartialQuery = !!q._selectedFieldNames;
|
|
141
|
+
const bms = isPartialQuery ? rows : await this.dbmsToBM(rows, opt);
|
|
227
142
|
return {
|
|
228
143
|
rows: bms,
|
|
229
144
|
...queryResult,
|
|
@@ -236,48 +151,27 @@ export class CommonDao {
|
|
|
236
151
|
async runQueryExtendedAsDBM(q, opt = {}) {
|
|
237
152
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
238
153
|
q.table = opt.table || q.table;
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
if (this.cfg.hooks.afterLoad && rows.length) {
|
|
243
|
-
rows = (await pMap(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
244
|
-
}
|
|
245
|
-
const partialQuery = !!q._selectedFieldNames;
|
|
246
|
-
const dbms = partialQuery ? rows : this.anyToDBMs(rows, opt);
|
|
247
|
-
this.logResult(started, op, dbms, q.table);
|
|
154
|
+
const { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
155
|
+
const isPartialQuery = !!q._selectedFieldNames;
|
|
156
|
+
const dbms = isPartialQuery ? rows : this.anyToDBMs(rows, opt);
|
|
248
157
|
return { rows: dbms, ...queryResult };
|
|
249
158
|
}
|
|
250
159
|
async runQueryCount(q, opt = {}) {
|
|
251
160
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
252
161
|
q.table = opt.table || q.table;
|
|
253
|
-
|
|
254
|
-
const started = this.logStarted(op, q.table);
|
|
255
|
-
const count = await this.cfg.db.runQueryCount(q, opt);
|
|
256
|
-
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
257
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
258
|
-
}
|
|
259
|
-
return count;
|
|
162
|
+
return await this.cfg.db.runQueryCount(q, opt);
|
|
260
163
|
}
|
|
261
164
|
async streamQueryForEach(q, mapper, opt = {}) {
|
|
262
165
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
263
166
|
q.table = opt.table || q.table;
|
|
264
167
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
265
168
|
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
266
|
-
const
|
|
267
|
-
const op = `streamQueryForEach(${q.pretty()})`;
|
|
268
|
-
const started = this.logStarted(op, q.table, true);
|
|
269
|
-
let count = 0;
|
|
169
|
+
const isPartialQuery = !!q._selectedFieldNames;
|
|
270
170
|
await _pipeline([
|
|
271
171
|
this.cfg.db.streamQuery(q, opt),
|
|
272
172
|
transformMap(async (dbm) => {
|
|
273
|
-
|
|
274
|
-
if (partialQuery)
|
|
173
|
+
if (isPartialQuery)
|
|
275
174
|
return dbm;
|
|
276
|
-
if (this.cfg.hooks.afterLoad) {
|
|
277
|
-
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
278
|
-
if (dbm === null)
|
|
279
|
-
return SKIP;
|
|
280
|
-
}
|
|
281
175
|
return await this.dbmToBM(dbm, opt);
|
|
282
176
|
}, {
|
|
283
177
|
errorMode: opt.errorMode,
|
|
@@ -293,30 +187,18 @@ export class CommonDao {
|
|
|
293
187
|
}),
|
|
294
188
|
writableVoid(),
|
|
295
189
|
]);
|
|
296
|
-
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
297
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
298
|
-
}
|
|
299
190
|
}
|
|
300
191
|
async streamQueryAsDBMForEach(q, mapper, opt = {}) {
|
|
301
192
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
302
193
|
q.table = opt.table || q.table;
|
|
303
194
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
304
195
|
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
305
|
-
const
|
|
306
|
-
const op = `streamQueryAsDBMForEach(${q.pretty()})`;
|
|
307
|
-
const started = this.logStarted(op, q.table, true);
|
|
308
|
-
let count = 0;
|
|
196
|
+
const isPartialQuery = !!q._selectedFieldNames;
|
|
309
197
|
await _pipeline([
|
|
310
198
|
this.cfg.db.streamQuery(q, opt),
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (partialQuery)
|
|
199
|
+
transformMapSync(dbm => {
|
|
200
|
+
if (isPartialQuery)
|
|
314
201
|
return dbm;
|
|
315
|
-
if (this.cfg.hooks.afterLoad) {
|
|
316
|
-
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
317
|
-
if (dbm === null)
|
|
318
|
-
return SKIP;
|
|
319
|
-
}
|
|
320
202
|
return this.anyToDBM(dbm, opt);
|
|
321
203
|
}, {
|
|
322
204
|
errorMode: opt.errorMode,
|
|
@@ -332,9 +214,6 @@ export class CommonDao {
|
|
|
332
214
|
}),
|
|
333
215
|
writableVoid(),
|
|
334
216
|
]);
|
|
335
|
-
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
336
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
337
|
-
}
|
|
338
217
|
}
|
|
339
218
|
/**
|
|
340
219
|
* Stream as Readable, to be able to .pipe() it further with support of backpressure.
|
|
@@ -344,19 +223,14 @@ export class CommonDao {
|
|
|
344
223
|
q.table = opt.table || q.table;
|
|
345
224
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
346
225
|
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
347
|
-
const
|
|
226
|
+
const isPartialQuery = !!q._selectedFieldNames;
|
|
348
227
|
const stream = this.cfg.db.streamQuery(q, opt);
|
|
349
|
-
if (
|
|
228
|
+
if (isPartialQuery)
|
|
350
229
|
return stream;
|
|
351
230
|
return (stream
|
|
352
231
|
// the commented out line was causing RangeError: Maximum call stack size exceeded
|
|
353
232
|
// .on('error', err => stream.emit('error', err))
|
|
354
|
-
.pipe(
|
|
355
|
-
if (this.cfg.hooks.afterLoad) {
|
|
356
|
-
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
357
|
-
if (dbm === null)
|
|
358
|
-
return SKIP;
|
|
359
|
-
}
|
|
233
|
+
.pipe(transformMapSync(dbm => {
|
|
360
234
|
return this.anyToDBM(dbm, opt);
|
|
361
235
|
}, {
|
|
362
236
|
errorMode: opt.errorMode,
|
|
@@ -377,16 +251,11 @@ export class CommonDao {
|
|
|
377
251
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
378
252
|
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
379
253
|
const stream = this.cfg.db.streamQuery(q, opt);
|
|
380
|
-
const
|
|
381
|
-
if (
|
|
254
|
+
const isPartialQuery = !!q._selectedFieldNames;
|
|
255
|
+
if (isPartialQuery)
|
|
382
256
|
return stream;
|
|
383
257
|
// This almost works, but hard to implement `errorMode: THROW_AGGREGATED` in this case
|
|
384
258
|
// return stream.flatMap(async (dbm: DBM) => {
|
|
385
|
-
// if (this.cfg.hooks!.afterLoad) {
|
|
386
|
-
// dbm = (await this.cfg.hooks!.afterLoad(dbm))!
|
|
387
|
-
// if (dbm === null) return [] // SKIP
|
|
388
|
-
// }
|
|
389
|
-
//
|
|
390
259
|
// return [await this.dbmToBM(dbm, opt)] satisfies BM[]
|
|
391
260
|
// }, {
|
|
392
261
|
// concurrency: 16,
|
|
@@ -398,11 +267,6 @@ export class CommonDao {
|
|
|
398
267
|
// the commented out line was causing RangeError: Maximum call stack size exceeded
|
|
399
268
|
// .on('error', err => stream.emit('error', err))
|
|
400
269
|
.pipe(transformMap(async (dbm) => {
|
|
401
|
-
if (this.cfg.hooks.afterLoad) {
|
|
402
|
-
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
403
|
-
if (dbm === null)
|
|
404
|
-
return SKIP;
|
|
405
|
-
}
|
|
406
270
|
return await this.dbmToBM(dbm, opt);
|
|
407
271
|
}, {
|
|
408
272
|
errorMode: opt.errorMode,
|
|
@@ -442,14 +306,8 @@ export class CommonDao {
|
|
|
442
306
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
443
307
|
q.table = opt.table || q.table;
|
|
444
308
|
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
445
|
-
const op = `streamQueryIdsForEach(${q.pretty()})`;
|
|
446
|
-
const started = this.logStarted(op, q.table, true);
|
|
447
|
-
let count = 0;
|
|
448
309
|
await _pipeline([
|
|
449
|
-
this.cfg.db.streamQuery(q.select(['id']), opt).map(r =>
|
|
450
|
-
count++;
|
|
451
|
-
return r.id;
|
|
452
|
-
}),
|
|
310
|
+
this.cfg.db.streamQuery(q.select(['id']), opt).map(r => r.id),
|
|
453
311
|
transformMap(mapper, {
|
|
454
312
|
...opt,
|
|
455
313
|
predicate: _passthroughPredicate,
|
|
@@ -461,13 +319,9 @@ export class CommonDao {
|
|
|
461
319
|
}),
|
|
462
320
|
writableVoid(),
|
|
463
321
|
]);
|
|
464
|
-
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
465
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} id(s) in ${_since(started)}`);
|
|
466
|
-
}
|
|
467
322
|
}
|
|
468
323
|
/**
|
|
469
324
|
* Mutates!
|
|
470
|
-
* "Returns", just to have a type of "Saved"
|
|
471
325
|
*/
|
|
472
326
|
assignIdCreatedUpdated(obj, opt = {}) {
|
|
473
327
|
const now = localTime.nowUnix();
|
|
@@ -481,7 +335,6 @@ export class CommonDao {
|
|
|
481
335
|
obj.id ||= (this.cfg.hooks.createNaturalId?.(obj) ||
|
|
482
336
|
this.cfg.hooks.createRandomId());
|
|
483
337
|
}
|
|
484
|
-
return obj;
|
|
485
338
|
}
|
|
486
339
|
// SAVE
|
|
487
340
|
/**
|
|
@@ -599,7 +452,6 @@ export class CommonDao {
|
|
|
599
452
|
return bm;
|
|
600
453
|
}
|
|
601
454
|
}
|
|
602
|
-
const idWasGenerated = !bm.id && this.cfg.generateId;
|
|
603
455
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
604
456
|
_typeCast(bm);
|
|
605
457
|
let dbm = await this.bmToDBM(bm, opt); // validates BM
|
|
@@ -609,125 +461,76 @@ export class CommonDao {
|
|
|
609
461
|
return bm;
|
|
610
462
|
}
|
|
611
463
|
const table = opt.table || this.cfg.table;
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
if (
|
|
615
|
-
opt = { ...opt, saveMethod: 'insert' };
|
|
616
|
-
}
|
|
617
|
-
const op = `save(${dbm.id})`;
|
|
618
|
-
const started = this.logSaveStarted(op, bm, table);
|
|
619
|
-
const { excludeFromIndexes } = this.cfg;
|
|
620
|
-
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
621
|
-
await (opt.tx || this.cfg.db).saveBatch(table, [dbm], {
|
|
622
|
-
excludeFromIndexes,
|
|
623
|
-
assignGeneratedIds,
|
|
624
|
-
...opt,
|
|
625
|
-
});
|
|
626
|
-
if (assignGeneratedIds) {
|
|
464
|
+
const saveOptions = this.prepareSaveOptions(opt);
|
|
465
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [dbm], saveOptions);
|
|
466
|
+
if (saveOptions.assignGeneratedIds) {
|
|
627
467
|
bm.id = dbm.id;
|
|
628
468
|
}
|
|
629
|
-
this.logSaveResult(started, op, table);
|
|
630
469
|
return bm;
|
|
631
470
|
}
|
|
632
471
|
async saveAsDBM(dbm, opt = {}) {
|
|
633
472
|
this.requireWriteAccess();
|
|
634
|
-
const table = opt.table || this.cfg.table;
|
|
635
|
-
// assigning id in case it misses the id
|
|
636
|
-
// will override/set `updated` field, unless opts.preserveUpdated is set
|
|
637
|
-
const idWasGenerated = !dbm.id && this.cfg.generateId;
|
|
638
473
|
this.assignIdCreatedUpdated(dbm, opt); // mutates
|
|
639
474
|
let row = this.anyToDBM(dbm, opt);
|
|
640
|
-
if (opt.ensureUniqueId && idWasGenerated)
|
|
641
|
-
await this.ensureUniqueId(table, row);
|
|
642
|
-
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
643
|
-
opt = { ...opt, saveMethod: 'insert' };
|
|
644
|
-
}
|
|
645
|
-
const op = `saveAsDBM(${row.id})`;
|
|
646
|
-
const started = this.logSaveStarted(op, row, table);
|
|
647
|
-
const { excludeFromIndexes } = this.cfg;
|
|
648
|
-
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
649
475
|
if (this.cfg.hooks.beforeSave) {
|
|
650
476
|
row = (await this.cfg.hooks.beforeSave(row));
|
|
651
477
|
if (row === null)
|
|
652
478
|
return dbm;
|
|
653
479
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
});
|
|
659
|
-
if (assignGeneratedIds) {
|
|
480
|
+
const table = opt.table || this.cfg.table;
|
|
481
|
+
const saveOptions = this.prepareSaveOptions(opt);
|
|
482
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [row], saveOptions);
|
|
483
|
+
if (saveOptions.assignGeneratedIds) {
|
|
660
484
|
dbm.id = row.id;
|
|
661
485
|
}
|
|
662
|
-
this.logSaveResult(started, op, table);
|
|
663
486
|
return row;
|
|
664
487
|
}
|
|
665
488
|
async saveBatch(bms, opt = {}) {
|
|
666
489
|
if (!bms.length)
|
|
667
490
|
return [];
|
|
668
491
|
this.requireWriteAccess();
|
|
669
|
-
const table = opt.table || this.cfg.table;
|
|
670
492
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt));
|
|
671
493
|
let dbms = await this.bmsToDBM(bms, opt);
|
|
672
494
|
if (this.cfg.hooks.beforeSave && dbms.length) {
|
|
673
495
|
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.beforeSave(dbm))).filter(_isTruthy);
|
|
674
496
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
}
|
|
680
|
-
const op = `saveBatch ${dbms.length} row(s) (${_truncate(dbms
|
|
681
|
-
.slice(0, 10)
|
|
682
|
-
.map(bm => bm.id)
|
|
683
|
-
.join(', '), 50)})`;
|
|
684
|
-
const started = this.logSaveStarted(op, bms, table);
|
|
685
|
-
const { excludeFromIndexes } = this.cfg;
|
|
686
|
-
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
687
|
-
await (opt.tx || this.cfg.db).saveBatch(table, dbms, {
|
|
688
|
-
excludeFromIndexes,
|
|
689
|
-
assignGeneratedIds,
|
|
690
|
-
...opt,
|
|
691
|
-
});
|
|
692
|
-
if (assignGeneratedIds) {
|
|
497
|
+
const table = opt.table || this.cfg.table;
|
|
498
|
+
const saveOptions = this.prepareSaveOptions(opt);
|
|
499
|
+
await (opt.tx || this.cfg.db).saveBatch(table, dbms, saveOptions);
|
|
500
|
+
if (saveOptions.assignGeneratedIds) {
|
|
693
501
|
dbms.forEach((dbm, i) => (bms[i].id = dbm.id));
|
|
694
502
|
}
|
|
695
|
-
this.logSaveResult(started, op, table);
|
|
696
503
|
return bms;
|
|
697
504
|
}
|
|
698
505
|
async saveBatchAsDBM(dbms, opt = {}) {
|
|
699
506
|
if (!dbms.length)
|
|
700
507
|
return [];
|
|
701
508
|
this.requireWriteAccess();
|
|
702
|
-
|
|
703
|
-
dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt)); // mutates
|
|
509
|
+
dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt));
|
|
704
510
|
let rows = this.anyToDBMs(dbms, opt);
|
|
705
|
-
if (opt.ensureUniqueId)
|
|
706
|
-
throw new AppError('ensureUniqueId is not supported in saveBatch');
|
|
707
|
-
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
708
|
-
opt = { ...opt, saveMethod: 'insert' };
|
|
709
|
-
}
|
|
710
|
-
const op = `saveBatchAsDBM ${rows.length} row(s) (${_truncate(rows
|
|
711
|
-
.slice(0, 10)
|
|
712
|
-
.map(bm => bm.id)
|
|
713
|
-
.join(', '), 50)})`;
|
|
714
|
-
const started = this.logSaveStarted(op, rows, table);
|
|
715
|
-
const { excludeFromIndexes } = this.cfg;
|
|
716
|
-
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
717
511
|
if (this.cfg.hooks.beforeSave && rows.length) {
|
|
718
512
|
rows = (await pMap(rows, async (row) => await this.cfg.hooks.beforeSave(row))).filter(_isTruthy);
|
|
719
513
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
});
|
|
725
|
-
if (assignGeneratedIds) {
|
|
514
|
+
const table = opt.table || this.cfg.table;
|
|
515
|
+
const saveOptions = this.prepareSaveOptions(opt);
|
|
516
|
+
await (opt.tx || this.cfg.db).saveBatch(table, rows, saveOptions);
|
|
517
|
+
if (saveOptions.assignGeneratedIds) {
|
|
726
518
|
rows.forEach((row, i) => (dbms[i].id = row.id));
|
|
727
519
|
}
|
|
728
|
-
this.logSaveResult(started, op, table);
|
|
729
520
|
return rows;
|
|
730
521
|
}
|
|
522
|
+
prepareSaveOptions(opt) {
|
|
523
|
+
let { saveMethod, assignGeneratedIds = this.cfg.assignGeneratedIds, excludeFromIndexes = this.cfg.excludeFromIndexes, } = opt;
|
|
524
|
+
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
525
|
+
saveMethod = 'insert';
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
...opt,
|
|
529
|
+
excludeFromIndexes,
|
|
530
|
+
saveMethod,
|
|
531
|
+
assignGeneratedIds,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
731
534
|
/**
|
|
732
535
|
* "Streaming" is implemented by buffering incoming rows into **batches**
|
|
733
536
|
* (of size opt.chunkSize, which defaults to 500),
|
|
@@ -793,12 +596,8 @@ export class CommonDao {
|
|
|
793
596
|
return 0;
|
|
794
597
|
this.requireWriteAccess();
|
|
795
598
|
this.requireObjectMutability(opt);
|
|
796
|
-
const op = `deleteByIds(${ids.join(', ')})`;
|
|
797
599
|
const table = opt.table || this.cfg.table;
|
|
798
|
-
|
|
799
|
-
const count = await (opt.tx || this.cfg.db).deleteByIds(table, ids, opt);
|
|
800
|
-
this.logSaveResult(started, op, table);
|
|
801
|
-
return count;
|
|
600
|
+
return await (opt.tx || this.cfg.db).deleteByIds(table, ids, opt);
|
|
802
601
|
}
|
|
803
602
|
/**
|
|
804
603
|
* Pass `chunkSize: number` (e.g 500) option to use Streaming: it will Stream the query, chunk by 500, and execute
|
|
@@ -810,8 +609,6 @@ export class CommonDao {
|
|
|
810
609
|
this.requireWriteAccess();
|
|
811
610
|
this.requireObjectMutability(opt);
|
|
812
611
|
q.table = opt.table || q.table;
|
|
813
|
-
const op = `deleteByQuery(${q.pretty()})`;
|
|
814
|
-
const started = this.logStarted(op, q.table);
|
|
815
612
|
let deleted = 0;
|
|
816
613
|
if (opt.chunkSize) {
|
|
817
614
|
const { chunkSize, chunkConcurrency = 8 } = opt;
|
|
@@ -839,7 +636,6 @@ export class CommonDao {
|
|
|
839
636
|
else {
|
|
840
637
|
deleted = await this.cfg.db.deleteByQuery(q, opt);
|
|
841
638
|
}
|
|
842
|
-
this.logSaveResult(started, op, q.table);
|
|
843
639
|
return deleted;
|
|
844
640
|
}
|
|
845
641
|
async patchByIds(ids, patch, opt = {}) {
|
|
@@ -852,11 +648,7 @@ export class CommonDao {
|
|
|
852
648
|
this.requireWriteAccess();
|
|
853
649
|
this.requireObjectMutability(opt);
|
|
854
650
|
q.table = opt.table || q.table;
|
|
855
|
-
|
|
856
|
-
const started = this.logStarted(op, q.table);
|
|
857
|
-
const updated = await this.cfg.db.patchByQuery(q, patch, opt);
|
|
858
|
-
this.logSaveResult(started, op, q.table);
|
|
859
|
-
return updated;
|
|
651
|
+
return await this.cfg.db.patchByQuery(q, patch, opt);
|
|
860
652
|
}
|
|
861
653
|
/**
|
|
862
654
|
* Caveat: it doesn't update created/updated props.
|
|
@@ -867,12 +659,9 @@ export class CommonDao {
|
|
|
867
659
|
this.requireWriteAccess();
|
|
868
660
|
this.requireObjectMutability(opt);
|
|
869
661
|
const { table } = this.cfg;
|
|
870
|
-
const op = `increment`;
|
|
871
|
-
const started = this.logStarted(op, table);
|
|
872
662
|
const result = await this.cfg.db.incrementBatch(table, prop, {
|
|
873
663
|
[id]: by,
|
|
874
664
|
});
|
|
875
|
-
this.logSaveResult(started, op, table);
|
|
876
665
|
return result[id];
|
|
877
666
|
}
|
|
878
667
|
/**
|
|
@@ -884,15 +673,11 @@ export class CommonDao {
|
|
|
884
673
|
this.requireWriteAccess();
|
|
885
674
|
this.requireObjectMutability(opt);
|
|
886
675
|
const { table } = this.cfg;
|
|
887
|
-
|
|
888
|
-
const started = this.logStarted(op, table);
|
|
889
|
-
const result = await this.cfg.db.incrementBatch(table, prop, incrementMap);
|
|
890
|
-
this.logSaveResult(started, op, table);
|
|
891
|
-
return result;
|
|
676
|
+
return await this.cfg.db.incrementBatch(table, prop, incrementMap);
|
|
892
677
|
}
|
|
893
678
|
async dbmToBM(_dbm, opt = {}) {
|
|
894
679
|
if (!_dbm)
|
|
895
|
-
return;
|
|
680
|
+
return null;
|
|
896
681
|
// optimization: no need to run full joi DBM validation, cause BM validation will be run
|
|
897
682
|
// const dbm = this.anyToDBM(_dbm, opt)
|
|
898
683
|
let dbm = { ..._dbm, ...this.cfg.hooks.parseNaturalId(_dbm.id) };
|
|
@@ -909,7 +694,7 @@ export class CommonDao {
|
|
|
909
694
|
}
|
|
910
695
|
async bmToDBM(bm, opt) {
|
|
911
696
|
if (bm === undefined)
|
|
912
|
-
return;
|
|
697
|
+
return null;
|
|
913
698
|
// bm gets assigned to the new reference
|
|
914
699
|
bm = this.validateAndConvert(bm, 'save', opt);
|
|
915
700
|
// BM > DBM
|
|
@@ -921,7 +706,7 @@ export class CommonDao {
|
|
|
921
706
|
}
|
|
922
707
|
anyToDBM(dbm, opt = {}) {
|
|
923
708
|
if (!dbm)
|
|
924
|
-
return;
|
|
709
|
+
return null;
|
|
925
710
|
// this shouldn't be happening on load! but should on save!
|
|
926
711
|
// this.assignIdCreatedUpdated(dbm, opt)
|
|
927
712
|
dbm = { ...dbm, ...this.cfg.hooks.parseNaturalId(dbm.id) };
|
|
@@ -934,8 +719,8 @@ export class CommonDao {
|
|
|
934
719
|
// return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
|
|
935
720
|
return dbm;
|
|
936
721
|
}
|
|
937
|
-
anyToDBMs(
|
|
938
|
-
return
|
|
722
|
+
anyToDBMs(rows, opt = {}) {
|
|
723
|
+
return rows.map(entity => this.anyToDBM(entity, opt));
|
|
939
724
|
}
|
|
940
725
|
/**
|
|
941
726
|
* Returns *converted value* (NOT the same reference).
|
|
@@ -994,10 +779,17 @@ export class CommonDao {
|
|
|
994
779
|
ids,
|
|
995
780
|
};
|
|
996
781
|
}
|
|
997
|
-
|
|
782
|
+
withRows(rows) {
|
|
783
|
+
return {
|
|
784
|
+
dao: this,
|
|
785
|
+
rows: rows,
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
withRow(row, opt) {
|
|
998
789
|
return {
|
|
999
790
|
dao: this,
|
|
1000
|
-
|
|
791
|
+
row: row,
|
|
792
|
+
opt: opt,
|
|
1001
793
|
};
|
|
1002
794
|
}
|
|
1003
795
|
/**
|
|
@@ -1091,10 +883,31 @@ export class CommonDao {
|
|
|
1091
883
|
return;
|
|
1092
884
|
const { db } = inputs[0].dao.cfg;
|
|
1093
885
|
const dbmsByTable = {};
|
|
1094
|
-
await pMap(inputs, async (
|
|
886
|
+
await pMap(inputs, async (input) => {
|
|
887
|
+
const { dao } = input;
|
|
1095
888
|
const { table } = dao.cfg;
|
|
1096
|
-
|
|
1097
|
-
|
|
889
|
+
dbmsByTable[table] ||= [];
|
|
890
|
+
if ('row' in input) {
|
|
891
|
+
// Singular
|
|
892
|
+
const { row } = input;
|
|
893
|
+
if (input.opt?.skipIfEquals) {
|
|
894
|
+
// We compare with convertedBM, to account for cases when some extra property is assigned to bm,
|
|
895
|
+
// which should be removed post-validation, but it breaks the "equality check"
|
|
896
|
+
// Post-validation the equality check should work as intended
|
|
897
|
+
const convertedBM = dao.validateAndConvert(row, 'save', opt);
|
|
898
|
+
if (_deepJsonEquals(convertedBM, input.opt.skipIfEquals)) {
|
|
899
|
+
// Skipping the save operation
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
dao.assignIdCreatedUpdated(row, opt);
|
|
904
|
+
dbmsByTable[table].push(await dao.bmToDBM(row, opt));
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
// Plural
|
|
908
|
+
input.rows.forEach(bm => dao.assignIdCreatedUpdated(bm, opt));
|
|
909
|
+
dbmsByTable[table].push(...(await dao.bmsToDBM(input.rows, opt)));
|
|
910
|
+
}
|
|
1098
911
|
});
|
|
1099
912
|
await db.multiSave(dbmsByTable);
|
|
1100
913
|
}
|
|
@@ -1116,6 +929,30 @@ export class CommonDao {
|
|
|
1116
929
|
}, opt);
|
|
1117
930
|
return r;
|
|
1118
931
|
}
|
|
932
|
+
ensureRequired(row, id, opt) {
|
|
933
|
+
const table = opt.table || this.cfg.table;
|
|
934
|
+
_assert(row, `DB row required, but not found in ${table}`, {
|
|
935
|
+
table,
|
|
936
|
+
id,
|
|
937
|
+
});
|
|
938
|
+
return row; // pass-through
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Throws if readOnly is true
|
|
942
|
+
*/
|
|
943
|
+
requireWriteAccess() {
|
|
944
|
+
_assert(!this.cfg.readOnly, DBLibError.DAO_IS_READ_ONLY, {
|
|
945
|
+
table: this.cfg.table,
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Throws if readOnly is true
|
|
950
|
+
*/
|
|
951
|
+
requireObjectMutability(opt) {
|
|
952
|
+
_assert(!this.cfg.immutable || opt.allowMutability, DBLibError.OBJECT_IS_IMMUTABLE, {
|
|
953
|
+
table: this.cfg.table,
|
|
954
|
+
});
|
|
955
|
+
}
|
|
1119
956
|
/**
|
|
1120
957
|
* Throws if query uses a property that is in `excludeFromIndexes` list.
|
|
1121
958
|
*/
|
|
@@ -1129,135 +966,4 @@ export class CommonDao {
|
|
|
1129
966
|
});
|
|
1130
967
|
}
|
|
1131
968
|
}
|
|
1132
|
-
logResult(started, op, res, table) {
|
|
1133
|
-
if (!this.cfg.logLevel)
|
|
1134
|
-
return;
|
|
1135
|
-
let logRes;
|
|
1136
|
-
const args = [];
|
|
1137
|
-
if (Array.isArray(res)) {
|
|
1138
|
-
logRes = `${res.length} row(s)`;
|
|
1139
|
-
if (res.length && this.cfg.logLevel >= CommonDaoLogLevel.DATA_FULL) {
|
|
1140
|
-
args.push('\n', ...res.slice(0, 10)); // max 10 items
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
else if (res) {
|
|
1144
|
-
logRes = `1 row`;
|
|
1145
|
-
if (this.cfg.logLevel >= CommonDaoLogLevel.DATA_SINGLE) {
|
|
1146
|
-
args.push('\n', res);
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
else {
|
|
1150
|
-
logRes = `undefined`;
|
|
1151
|
-
}
|
|
1152
|
-
this.cfg.logger?.log(`<< ${table}.${op}: ${logRes} in ${_since(started)}`, ...args);
|
|
1153
|
-
}
|
|
1154
|
-
logSaveResult(started, op, table) {
|
|
1155
|
-
if (!this.cfg.logLevel)
|
|
1156
|
-
return;
|
|
1157
|
-
this.cfg.logger?.log(`<< ${table}.${op} in ${_since(started)}`);
|
|
1158
|
-
}
|
|
1159
|
-
logStarted(op, table, force = false) {
|
|
1160
|
-
if (this.cfg.logStarted || force) {
|
|
1161
|
-
this.cfg.logger?.log(`>> ${table}.${op}`);
|
|
1162
|
-
}
|
|
1163
|
-
return localTime.nowUnixMillis();
|
|
1164
|
-
}
|
|
1165
|
-
logSaveStarted(op, items, table) {
|
|
1166
|
-
if (this.cfg.logStarted) {
|
|
1167
|
-
const args = [`>> ${table}.${op}`];
|
|
1168
|
-
if (Array.isArray(items)) {
|
|
1169
|
-
if (items.length && this.cfg.logLevel >= CommonDaoLogLevel.DATA_FULL) {
|
|
1170
|
-
args.push('\n', ...items.slice(0, 10));
|
|
1171
|
-
}
|
|
1172
|
-
else {
|
|
1173
|
-
args.push(`${items.length} row(s)`);
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
else {
|
|
1177
|
-
if (this.cfg.logLevel >= CommonDaoLogLevel.DATA_SINGLE) {
|
|
1178
|
-
args.push(items);
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
this.cfg.logger?.log(...args);
|
|
1182
|
-
}
|
|
1183
|
-
return localTime.nowUnixMillis();
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
/**
|
|
1187
|
-
* Transaction context.
|
|
1188
|
-
* Has similar API than CommonDao, but all operations are performed in the context of the transaction.
|
|
1189
|
-
*/
|
|
1190
|
-
export class CommonDaoTransaction {
|
|
1191
|
-
tx;
|
|
1192
|
-
logger;
|
|
1193
|
-
constructor(tx, logger) {
|
|
1194
|
-
this.tx = tx;
|
|
1195
|
-
this.logger = logger;
|
|
1196
|
-
}
|
|
1197
|
-
/**
|
|
1198
|
-
* Commits the underlying DBTransaction.
|
|
1199
|
-
* May throw.
|
|
1200
|
-
*/
|
|
1201
|
-
async commit() {
|
|
1202
|
-
await this.tx.commit();
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Perform a graceful rollback without throwing/re-throwing any error.
|
|
1206
|
-
* Never throws.
|
|
1207
|
-
*/
|
|
1208
|
-
async rollback() {
|
|
1209
|
-
try {
|
|
1210
|
-
await this.tx.rollback();
|
|
1211
|
-
}
|
|
1212
|
-
catch (err) {
|
|
1213
|
-
// graceful rollback without re-throw
|
|
1214
|
-
this.logger.error(err);
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
async getById(dao, id, opt) {
|
|
1218
|
-
return await dao.getById(id, { ...opt, tx: this.tx });
|
|
1219
|
-
}
|
|
1220
|
-
async getByIds(dao, ids, opt) {
|
|
1221
|
-
return await dao.getByIds(ids, { ...opt, tx: this.tx });
|
|
1222
|
-
}
|
|
1223
|
-
// todo: Queries inside Transaction are not supported yet
|
|
1224
|
-
// async runQuery<BM extends PartialObjectWithId, DBM extends ObjectWithId>(
|
|
1225
|
-
// dao: CommonDao<BM, DBM, any>,
|
|
1226
|
-
// q: DBQuery<DBM>,
|
|
1227
|
-
// opt?: CommonDaoOptions,
|
|
1228
|
-
// ): Promise<BM[]> {
|
|
1229
|
-
// try {
|
|
1230
|
-
// return await dao.runQuery(q, { ...opt, tx: this.tx })
|
|
1231
|
-
// } catch (err) {
|
|
1232
|
-
// await this.rollback()
|
|
1233
|
-
// throw err
|
|
1234
|
-
// }
|
|
1235
|
-
// }
|
|
1236
|
-
async save(dao, bm, opt) {
|
|
1237
|
-
return await dao.save(bm, { ...opt, tx: this.tx });
|
|
1238
|
-
}
|
|
1239
|
-
async saveBatch(dao, bms, opt) {
|
|
1240
|
-
return await dao.saveBatch(bms, { ...opt, tx: this.tx });
|
|
1241
|
-
}
|
|
1242
|
-
/**
|
|
1243
|
-
* DaoTransaction.patch does not load from DB.
|
|
1244
|
-
* It assumes the bm was previously loaded in the same Transaction, hence could not be
|
|
1245
|
-
* concurrently modified. Hence it's safe to not sync with DB.
|
|
1246
|
-
*
|
|
1247
|
-
* So, this method is a rather simple convenience "Object.assign and then save".
|
|
1248
|
-
*/
|
|
1249
|
-
async patch(dao, bm, patch, opt) {
|
|
1250
|
-
const skipIfEquals = _deepCopy(bm);
|
|
1251
|
-
Object.assign(bm, patch);
|
|
1252
|
-
return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx });
|
|
1253
|
-
}
|
|
1254
|
-
// todo: use AnyDao/Infer in other methods as well, if this works well
|
|
1255
|
-
async deleteById(dao, id, opt) {
|
|
1256
|
-
if (!id)
|
|
1257
|
-
return 0;
|
|
1258
|
-
return await this.deleteByIds(dao, [id], opt);
|
|
1259
|
-
}
|
|
1260
|
-
async deleteByIds(dao, ids, opt) {
|
|
1261
|
-
return await dao.deleteByIds(ids, { ...opt, tx: this.tx });
|
|
1262
|
-
}
|
|
1263
969
|
}
|