@naturalcycles/db-lib 10.0.1 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/cachedb/cache.db.d.ts +7 -7
- package/dist/adapter/cachedb/cache.db.js +13 -16
- package/dist/adapter/cachedb/cache.db.model.d.ts +3 -3
- package/dist/adapter/cachedb/cache.db.model.js +1 -2
- package/dist/adapter/cachedb/index.d.ts +2 -2
- package/dist/adapter/cachedb/index.js +2 -5
- package/dist/adapter/file/file.db.d.ts +7 -7
- package/dist/adapter/file/file.db.js +35 -36
- package/dist/adapter/file/file.db.model.d.ts +3 -3
- package/dist/adapter/file/file.db.model.js +1 -2
- package/dist/adapter/file/inMemory.persistence.plugin.d.ts +3 -3
- package/dist/adapter/file/inMemory.persistence.plugin.js +4 -10
- package/dist/adapter/file/index.d.ts +3 -3
- package/dist/adapter/file/index.js +3 -7
- package/dist/adapter/file/localFile.persistence.plugin.d.ts +3 -3
- package/dist/adapter/file/localFile.persistence.plugin.js +12 -15
- package/dist/adapter/file/noop.persistence.plugin.d.ts +3 -3
- package/dist/adapter/file/noop.persistence.plugin.js +1 -5
- package/dist/adapter/inmemory/inMemory.db.d.ts +7 -5
- package/dist/adapter/inmemory/inMemory.db.js +54 -49
- package/dist/adapter/inmemory/inMemoryKeyValueDB.d.ts +4 -4
- package/dist/adapter/inmemory/inMemoryKeyValueDB.js +12 -15
- package/dist/adapter/inmemory/queryInMemory.d.ts +2 -2
- package/dist/adapter/inmemory/queryInMemory.js +4 -7
- package/dist/base.common.db.d.ts +8 -5
- package/dist/base.common.db.js +9 -12
- package/dist/cnst.js +2 -5
- package/dist/common.db.d.ts +9 -3
- package/dist/common.db.js +3 -6
- package/dist/commondao/common.dao.d.ts +16 -6
- package/dist/commondao/common.dao.js +123 -112
- package/dist/commondao/common.dao.model.d.ts +5 -4
- package/dist/commondao/common.dao.model.js +2 -5
- package/dist/db.model.d.ts +7 -2
- package/dist/db.model.js +4 -7
- package/dist/index.d.ts +17 -17
- package/dist/index.js +17 -20
- package/dist/kv/commonKeyValueDB.d.ts +3 -3
- package/dist/kv/commonKeyValueDB.js +1 -4
- package/dist/kv/commonKeyValueDao.d.ts +5 -5
- package/dist/kv/commonKeyValueDao.js +11 -14
- package/dist/kv/commonKeyValueDaoMemoCache.d.ts +3 -2
- package/dist/kv/commonKeyValueDaoMemoCache.js +5 -8
- package/dist/model.util.d.ts +1 -1
- package/dist/model.util.js +9 -15
- package/dist/pipeline/dbPipelineBackup.d.ts +6 -4
- package/dist/pipeline/dbPipelineBackup.js +27 -30
- package/dist/pipeline/dbPipelineCopy.d.ts +6 -4
- package/dist/pipeline/dbPipelineCopy.js +20 -23
- package/dist/pipeline/dbPipelineRestore.d.ts +6 -4
- package/dist/pipeline/dbPipelineRestore.js +27 -30
- package/dist/query/dbQuery.d.ts +5 -5
- package/dist/query/dbQuery.js +22 -17
- package/dist/testing/{dbTest.d.ts → commonDBTest.d.ts} +1 -1
- package/dist/testing/{dbTest.js → commonDBTest.js} +81 -62
- package/dist/testing/commonDaoTest.d.ts +3 -0
- package/dist/testing/{daoTest.js → commonDaoTest.js} +101 -38
- package/dist/testing/index.d.ts +7 -5
- package/dist/testing/index.js +6 -20
- package/dist/testing/keyValueDBTest.d.ts +1 -1
- package/dist/testing/keyValueDBTest.js +28 -31
- package/dist/testing/keyValueDaoTest.d.ts +1 -1
- package/dist/testing/keyValueDaoTest.js +8 -11
- package/dist/testing/test.model.d.ts +1 -1
- package/dist/testing/test.model.js +30 -37
- package/dist/testing/timeSeriesTest.util.d.ts +1 -1
- package/dist/testing/timeSeriesTest.util.js +3 -6
- package/dist/timeseries/commonTimeSeriesDao.d.ts +1 -1
- package/dist/timeseries/commonTimeSeriesDao.js +6 -9
- package/dist/timeseries/timeSeries.model.d.ts +1 -1
- package/dist/timeseries/timeSeries.model.js +1 -2
- package/dist/transaction/dbTransaction.util.d.ts +4 -3
- package/dist/transaction/dbTransaction.util.js +3 -5
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +25 -28
- package/package.json +5 -4
- package/src/adapter/cachedb/cache.db.model.ts +4 -4
- package/src/adapter/cachedb/cache.db.ts +10 -9
- package/src/adapter/cachedb/index.ts +3 -3
- package/src/adapter/file/file.db.model.ts +3 -3
- package/src/adapter/file/file.db.ts +12 -16
- package/src/adapter/file/inMemory.persistence.plugin.ts +4 -3
- package/src/adapter/file/index.ts +3 -3
- package/src/adapter/file/localFile.persistence.plugin.ts +4 -3
- package/src/adapter/file/noop.persistence.plugin.ts +3 -3
- package/src/adapter/inmemory/inMemory.db.ts +26 -27
- package/src/adapter/inmemory/inMemoryKeyValueDB.ts +6 -6
- package/src/adapter/inmemory/queryInMemory.ts +3 -2
- package/src/base.common.db.ts +12 -7
- package/src/common.db.ts +12 -4
- package/src/commondao/common.dao.model.ts +5 -6
- package/src/commondao/common.dao.ts +41 -23
- package/src/db.model.ts +8 -2
- package/src/index.ts +17 -17
- package/src/kv/commonKeyValueDB.ts +3 -3
- package/src/kv/commonKeyValueDao.ts +8 -6
- package/src/kv/commonKeyValueDaoMemoCache.ts +3 -8
- package/src/model.util.ts +2 -1
- package/src/pipeline/dbPipelineBackup.ts +5 -14
- package/src/pipeline/dbPipelineCopy.ts +6 -14
- package/src/pipeline/dbPipelineRestore.ts +9 -8
- package/src/query/dbQuery.ts +7 -12
- package/src/testing/{dbTest.ts → commonDBTest.ts} +34 -5
- package/src/testing/{daoTest.ts → commonDaoTest.ts} +89 -11
- package/src/testing/index.ts +7 -8
- package/src/testing/keyValueDBTest.ts +4 -3
- package/src/testing/keyValueDaoTest.ts +3 -3
- package/src/testing/test.model.ts +2 -7
- package/src/testing/timeSeriesTest.util.ts +1 -1
- package/src/timeseries/commonTimeSeriesDao.ts +5 -4
- package/src/timeseries/timeSeries.model.ts +1 -1
- package/src/transaction/dbTransaction.util.ts +4 -3
- package/src/validation/index.ts +4 -4
- package/dist/testing/daoTest.d.ts +0 -3
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const dbQuery_1 = require("../query/dbQuery");
|
|
8
|
-
const common_dao_model_1 = require("./common.dao.model");
|
|
1
|
+
import { _assert, _deepCopy, _deepJsonEquals, _filterUndefinedValues, _isTruthy, _objectAssignExact, _passthroughPredicate, _since, _truncate, _typeCast, _uniqBy, AppError, ErrorMode, localTime, pMap, SKIP, } from '@naturalcycles/js-lib';
|
|
2
|
+
import { ZodSchema, zSafeValidate } from '@naturalcycles/js-lib/dist/zod/index.js';
|
|
3
|
+
import { _pipeline, AjvSchema, getValidationResult, stringId, transformChunk, transformLogProgress, transformMap, transformNoOp, writableVoid, } from '@naturalcycles/nodejs-lib';
|
|
4
|
+
import { DBLibError } from '../cnst.js';
|
|
5
|
+
import { RunnableDBQuery } from '../query/dbQuery.js';
|
|
6
|
+
import { CommonDaoLogLevel } from './common.dao.model.js';
|
|
9
7
|
const isGAE = !!process.env['GAE_INSTANCE'];
|
|
10
8
|
const isCI = !!process.env['CI'];
|
|
11
9
|
/**
|
|
@@ -15,14 +13,15 @@ const isCI = !!process.env['CI'];
|
|
|
15
13
|
* BM = Backend model (optimized for API access)
|
|
16
14
|
* TM = Transport model (optimized to be sent over the wire)
|
|
17
15
|
*/
|
|
18
|
-
class CommonDao {
|
|
16
|
+
export class CommonDao {
|
|
17
|
+
cfg;
|
|
19
18
|
constructor(cfg) {
|
|
20
19
|
this.cfg = cfg;
|
|
21
20
|
this.cfg = {
|
|
22
21
|
// Default is to NOT log in AppEngine and in CI,
|
|
23
22
|
// otherwise to log Operations
|
|
24
23
|
// e.g in Dev (local machine), Test - it will log operations (useful for debugging)
|
|
25
|
-
logLevel: isGAE || isCI ?
|
|
24
|
+
logLevel: isGAE || isCI ? CommonDaoLogLevel.NONE : CommonDaoLogLevel.OPERATIONS,
|
|
26
25
|
generateId: true,
|
|
27
26
|
assignGeneratedIds: false,
|
|
28
27
|
useCreatedProperty: true,
|
|
@@ -40,7 +39,7 @@ class CommonDao {
|
|
|
40
39
|
},
|
|
41
40
|
};
|
|
42
41
|
if (this.cfg.generateId) {
|
|
43
|
-
this.cfg.hooks.createRandomId ||= () =>
|
|
42
|
+
this.cfg.hooks.createRandomId ||= () => stringId();
|
|
44
43
|
}
|
|
45
44
|
else {
|
|
46
45
|
delete this.cfg.hooks.createRandomId;
|
|
@@ -96,12 +95,12 @@ class CommonDao {
|
|
|
96
95
|
async getByIds(ids, opt = {}) {
|
|
97
96
|
if (!ids.length)
|
|
98
97
|
return [];
|
|
99
|
-
const op = `getByIds ${ids.length} id(s) (${
|
|
98
|
+
const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`;
|
|
100
99
|
const table = opt.table || this.cfg.table;
|
|
101
100
|
const started = this.logStarted(op, table);
|
|
102
101
|
let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids, opt);
|
|
103
102
|
if (this.cfg.hooks.afterLoad && dbms.length) {
|
|
104
|
-
dbms = (await
|
|
103
|
+
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
105
104
|
}
|
|
106
105
|
const bms = await this.dbmsToBM(dbms, opt);
|
|
107
106
|
this.logResult(started, op, bms, table);
|
|
@@ -110,12 +109,12 @@ class CommonDao {
|
|
|
110
109
|
async getByIdsAsDBM(ids, opt = {}) {
|
|
111
110
|
if (!ids.length)
|
|
112
111
|
return [];
|
|
113
|
-
const op = `getByIdsAsDBM ${ids.length} id(s) (${
|
|
112
|
+
const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`;
|
|
114
113
|
const table = opt.table || this.cfg.table;
|
|
115
114
|
const started = this.logStarted(op, table);
|
|
116
115
|
let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids, opt);
|
|
117
116
|
if (this.cfg.hooks.afterLoad && dbms.length) {
|
|
118
|
-
dbms = (await
|
|
117
|
+
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
119
118
|
}
|
|
120
119
|
this.logResult(started, op, dbms, table);
|
|
121
120
|
return dbms;
|
|
@@ -136,7 +135,7 @@ class CommonDao {
|
|
|
136
135
|
}
|
|
137
136
|
throwRequiredError(id, opt) {
|
|
138
137
|
const table = opt.table || this.cfg.table;
|
|
139
|
-
throw new
|
|
138
|
+
throw new AppError(`DB row required, but not found in ${table}`, {
|
|
140
139
|
table,
|
|
141
140
|
id,
|
|
142
141
|
});
|
|
@@ -146,7 +145,7 @@ class CommonDao {
|
|
|
146
145
|
*/
|
|
147
146
|
requireWriteAccess() {
|
|
148
147
|
if (this.cfg.readOnly) {
|
|
149
|
-
throw new
|
|
148
|
+
throw new AppError(DBLibError.DAO_IS_READ_ONLY, {
|
|
150
149
|
table: this.cfg.table,
|
|
151
150
|
});
|
|
152
151
|
}
|
|
@@ -156,7 +155,7 @@ class CommonDao {
|
|
|
156
155
|
*/
|
|
157
156
|
requireObjectMutability(opt) {
|
|
158
157
|
if (this.cfg.immutable && !opt.allowMutability) {
|
|
159
|
-
throw new
|
|
158
|
+
throw new AppError(DBLibError.OBJECT_IS_IMMUTABLE, {
|
|
160
159
|
table: this.cfg.table,
|
|
161
160
|
});
|
|
162
161
|
}
|
|
@@ -165,7 +164,7 @@ class CommonDao {
|
|
|
165
164
|
// todo: retry N times
|
|
166
165
|
const existing = await this.cfg.db.getByIds(table, [dbm.id]);
|
|
167
166
|
if (existing.length) {
|
|
168
|
-
throw new
|
|
167
|
+
throw new AppError(DBLibError.NON_UNIQUE_ID, {
|
|
169
168
|
table,
|
|
170
169
|
ids: existing.map(i => i.id),
|
|
171
170
|
});
|
|
@@ -186,14 +185,14 @@ class CommonDao {
|
|
|
186
185
|
* Pass `table` to override table
|
|
187
186
|
*/
|
|
188
187
|
query(table) {
|
|
189
|
-
return new
|
|
188
|
+
return new RunnableDBQuery(this, table);
|
|
190
189
|
}
|
|
191
190
|
async runQuery(q, opt) {
|
|
192
191
|
const { rows } = await this.runQueryExtended(q, opt);
|
|
193
192
|
return rows;
|
|
194
193
|
}
|
|
195
194
|
async runQuerySingleColumn(q, opt) {
|
|
196
|
-
|
|
195
|
+
_assert(q._selectedFieldNames?.length === 1, `runQuerySingleColumn requires exactly 1 column to be selected: ${q.pretty()}`);
|
|
197
196
|
const col = q._selectedFieldNames[0];
|
|
198
197
|
const { rows } = await this.runQueryExtended(q, opt);
|
|
199
198
|
return rows.map((r) => r[col]);
|
|
@@ -204,8 +203,8 @@ class CommonDao {
|
|
|
204
203
|
* Order is not guaranteed, as queries run in parallel.
|
|
205
204
|
*/
|
|
206
205
|
async runUnionQueries(queries, opt) {
|
|
207
|
-
const results = (await
|
|
208
|
-
return
|
|
206
|
+
const results = (await pMap(queries, async (q) => (await this.runQueryExtended(q, opt)).rows)).flat();
|
|
207
|
+
return _uniqBy(results, r => r.id);
|
|
209
208
|
}
|
|
210
209
|
async runQueryExtended(q, opt = {}) {
|
|
211
210
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
@@ -215,7 +214,7 @@ class CommonDao {
|
|
|
215
214
|
let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
216
215
|
const partialQuery = !!q._selectedFieldNames;
|
|
217
216
|
if (this.cfg.hooks.afterLoad && rows.length) {
|
|
218
|
-
rows = (await
|
|
217
|
+
rows = (await pMap(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
219
218
|
}
|
|
220
219
|
const bms = partialQuery ? rows : await this.dbmsToBM(rows, opt);
|
|
221
220
|
this.logResult(started, op, bms, q.table);
|
|
@@ -235,7 +234,7 @@ class CommonDao {
|
|
|
235
234
|
const started = this.logStarted(op, q.table);
|
|
236
235
|
let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
237
236
|
if (this.cfg.hooks.afterLoad && rows.length) {
|
|
238
|
-
rows = (await
|
|
237
|
+
rows = (await pMap(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
239
238
|
}
|
|
240
239
|
const partialQuery = !!q._selectedFieldNames;
|
|
241
240
|
const dbms = partialQuery ? rows : this.anyToDBMs(rows, opt);
|
|
@@ -248,8 +247,8 @@ class CommonDao {
|
|
|
248
247
|
const op = `runQueryCount(${q.pretty()})`;
|
|
249
248
|
const started = this.logStarted(op, q.table);
|
|
250
249
|
const count = await this.cfg.db.runQueryCount(q, opt);
|
|
251
|
-
if (this.cfg.logLevel >=
|
|
252
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${
|
|
250
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
251
|
+
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
253
252
|
}
|
|
254
253
|
return count;
|
|
255
254
|
}
|
|
@@ -257,78 +256,78 @@ class CommonDao {
|
|
|
257
256
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
258
257
|
q.table = opt.table || q.table;
|
|
259
258
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
260
|
-
opt.errorMode ||=
|
|
259
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
261
260
|
const partialQuery = !!q._selectedFieldNames;
|
|
262
261
|
const op = `streamQueryForEach(${q.pretty()})`;
|
|
263
262
|
const started = this.logStarted(op, q.table, true);
|
|
264
263
|
let count = 0;
|
|
265
|
-
await
|
|
264
|
+
await _pipeline([
|
|
266
265
|
this.cfg.db.streamQuery(q, opt),
|
|
267
|
-
|
|
266
|
+
transformMap(async (dbm) => {
|
|
268
267
|
count++;
|
|
269
268
|
if (partialQuery)
|
|
270
269
|
return dbm;
|
|
271
270
|
if (this.cfg.hooks.afterLoad) {
|
|
272
271
|
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
273
272
|
if (dbm === null)
|
|
274
|
-
return
|
|
273
|
+
return SKIP;
|
|
275
274
|
}
|
|
276
275
|
return await this.dbmToBM(dbm, opt);
|
|
277
276
|
}, {
|
|
278
277
|
errorMode: opt.errorMode,
|
|
279
278
|
}),
|
|
280
|
-
|
|
279
|
+
transformMap(mapper, {
|
|
281
280
|
...opt,
|
|
282
|
-
predicate:
|
|
281
|
+
predicate: _passthroughPredicate, // to be able to logProgress
|
|
283
282
|
}),
|
|
284
283
|
// LogProgress should be AFTER the mapper, to be able to report correct stats
|
|
285
|
-
|
|
284
|
+
transformLogProgress({
|
|
286
285
|
metric: q.table,
|
|
287
286
|
...opt,
|
|
288
287
|
}),
|
|
289
|
-
|
|
288
|
+
writableVoid(),
|
|
290
289
|
]);
|
|
291
|
-
if (this.cfg.logLevel >=
|
|
292
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${
|
|
290
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
291
|
+
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
293
292
|
}
|
|
294
293
|
}
|
|
295
294
|
async streamQueryAsDBMForEach(q, mapper, opt = {}) {
|
|
296
295
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
297
296
|
q.table = opt.table || q.table;
|
|
298
297
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
299
|
-
opt.errorMode ||=
|
|
298
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
300
299
|
const partialQuery = !!q._selectedFieldNames;
|
|
301
300
|
const op = `streamQueryAsDBMForEach(${q.pretty()})`;
|
|
302
301
|
const started = this.logStarted(op, q.table, true);
|
|
303
302
|
let count = 0;
|
|
304
|
-
await
|
|
303
|
+
await _pipeline([
|
|
305
304
|
this.cfg.db.streamQuery(q, opt),
|
|
306
|
-
|
|
305
|
+
transformMap(async (dbm) => {
|
|
307
306
|
count++;
|
|
308
307
|
if (partialQuery)
|
|
309
308
|
return dbm;
|
|
310
309
|
if (this.cfg.hooks.afterLoad) {
|
|
311
310
|
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
312
311
|
if (dbm === null)
|
|
313
|
-
return
|
|
312
|
+
return SKIP;
|
|
314
313
|
}
|
|
315
314
|
return this.anyToDBM(dbm, opt);
|
|
316
315
|
}, {
|
|
317
316
|
errorMode: opt.errorMode,
|
|
318
317
|
}),
|
|
319
|
-
|
|
318
|
+
transformMap(mapper, {
|
|
320
319
|
...opt,
|
|
321
|
-
predicate:
|
|
320
|
+
predicate: _passthroughPredicate, // to be able to logProgress
|
|
322
321
|
}),
|
|
323
322
|
// LogProgress should be AFTER the mapper, to be able to report correct stats
|
|
324
|
-
|
|
323
|
+
transformLogProgress({
|
|
325
324
|
metric: q.table,
|
|
326
325
|
...opt,
|
|
327
326
|
}),
|
|
328
|
-
|
|
327
|
+
writableVoid(),
|
|
329
328
|
]);
|
|
330
|
-
if (this.cfg.logLevel >=
|
|
331
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${
|
|
329
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
330
|
+
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
332
331
|
}
|
|
333
332
|
}
|
|
334
333
|
/**
|
|
@@ -338,18 +337,18 @@ class CommonDao {
|
|
|
338
337
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
339
338
|
q.table = opt.table || q.table;
|
|
340
339
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
341
|
-
opt.errorMode ||=
|
|
340
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
342
341
|
const partialQuery = !!q._selectedFieldNames;
|
|
343
342
|
const stream = this.cfg.db.streamQuery(q, opt);
|
|
344
343
|
if (partialQuery)
|
|
345
344
|
return stream;
|
|
346
345
|
return stream
|
|
347
346
|
.on('error', err => stream.emit('error', err))
|
|
348
|
-
.pipe(
|
|
347
|
+
.pipe(transformMap(async (dbm) => {
|
|
349
348
|
if (this.cfg.hooks.afterLoad) {
|
|
350
349
|
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
351
350
|
if (dbm === null)
|
|
352
|
-
return
|
|
351
|
+
return SKIP;
|
|
353
352
|
}
|
|
354
353
|
return this.anyToDBM(dbm, opt);
|
|
355
354
|
}, {
|
|
@@ -369,7 +368,7 @@ class CommonDao {
|
|
|
369
368
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
370
369
|
q.table = opt.table || q.table;
|
|
371
370
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
372
|
-
opt.errorMode ||=
|
|
371
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
373
372
|
const stream = this.cfg.db.streamQuery(q, opt);
|
|
374
373
|
const partialQuery = !!q._selectedFieldNames;
|
|
375
374
|
if (partialQuery)
|
|
@@ -390,11 +389,11 @@ class CommonDao {
|
|
|
390
389
|
// .pipe(transformMap<any, DBM>(dbm => this.anyToDBM(dbm, opt), safeOpt))
|
|
391
390
|
// .pipe(transformMap<DBM, BM>(dbm => this.dbmToBM(dbm, opt), safeOpt))
|
|
392
391
|
.on('error', err => stream.emit('error', err))
|
|
393
|
-
.pipe(
|
|
392
|
+
.pipe(transformMap(async (dbm) => {
|
|
394
393
|
if (this.cfg.hooks.afterLoad) {
|
|
395
394
|
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
396
395
|
if (dbm === null)
|
|
397
|
-
return
|
|
396
|
+
return SKIP;
|
|
398
397
|
}
|
|
399
398
|
return await this.dbmToBM(dbm, opt);
|
|
400
399
|
}, {
|
|
@@ -404,7 +403,7 @@ class CommonDao {
|
|
|
404
403
|
// but not applying it now for perf reasons
|
|
405
404
|
// UPD: applying, to be compliant with `.toArray()`, etc.
|
|
406
405
|
.on('error', err => stream.emit('error', err))
|
|
407
|
-
.pipe(
|
|
406
|
+
.pipe(transformNoOp()));
|
|
408
407
|
}
|
|
409
408
|
async queryIds(q, opt = {}) {
|
|
410
409
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
@@ -415,7 +414,7 @@ class CommonDao {
|
|
|
415
414
|
streamQueryIds(q, opt = {}) {
|
|
416
415
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
417
416
|
q.table = opt.table || q.table;
|
|
418
|
-
opt.errorMode ||=
|
|
417
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
419
418
|
// Experimental: using `.map()`
|
|
420
419
|
const stream = this.cfg.db
|
|
421
420
|
.streamQuery(q.select(['id']), opt)
|
|
@@ -434,28 +433,28 @@ class CommonDao {
|
|
|
434
433
|
async streamQueryIdsForEach(q, mapper, opt = {}) {
|
|
435
434
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
436
435
|
q.table = opt.table || q.table;
|
|
437
|
-
opt.errorMode ||=
|
|
436
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
438
437
|
const op = `streamQueryIdsForEach(${q.pretty()})`;
|
|
439
438
|
const started = this.logStarted(op, q.table, true);
|
|
440
439
|
let count = 0;
|
|
441
|
-
await
|
|
440
|
+
await _pipeline([
|
|
442
441
|
this.cfg.db.streamQuery(q.select(['id']), opt).map(r => {
|
|
443
442
|
count++;
|
|
444
443
|
return r.id;
|
|
445
444
|
}),
|
|
446
|
-
|
|
445
|
+
transformMap(mapper, {
|
|
447
446
|
...opt,
|
|
448
|
-
predicate:
|
|
447
|
+
predicate: _passthroughPredicate,
|
|
449
448
|
}),
|
|
450
449
|
// LogProgress should be AFTER the mapper, to be able to report correct stats
|
|
451
|
-
|
|
450
|
+
transformLogProgress({
|
|
452
451
|
metric: q.table,
|
|
453
452
|
...opt,
|
|
454
453
|
}),
|
|
455
|
-
|
|
454
|
+
writableVoid(),
|
|
456
455
|
]);
|
|
457
|
-
if (this.cfg.logLevel >=
|
|
458
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} id(s) in ${
|
|
456
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
457
|
+
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} id(s) in ${_since(started)}`);
|
|
459
458
|
}
|
|
460
459
|
}
|
|
461
460
|
/**
|
|
@@ -463,7 +462,7 @@ class CommonDao {
|
|
|
463
462
|
* "Returns", just to have a type of "Saved"
|
|
464
463
|
*/
|
|
465
464
|
assignIdCreatedUpdated(obj, opt = {}) {
|
|
466
|
-
const now =
|
|
465
|
+
const now = localTime.nowUnix();
|
|
467
466
|
if (this.cfg.useCreatedProperty) {
|
|
468
467
|
obj.created ||= obj.updated || now;
|
|
469
468
|
}
|
|
@@ -502,14 +501,14 @@ class CommonDao {
|
|
|
502
501
|
});
|
|
503
502
|
if (loaded) {
|
|
504
503
|
patched = { ...loaded, ...patch };
|
|
505
|
-
if (
|
|
504
|
+
if (_deepJsonEquals(loaded, patched)) {
|
|
506
505
|
// Skipping the save operation, as data is the same
|
|
507
506
|
return patched;
|
|
508
507
|
}
|
|
509
508
|
}
|
|
510
509
|
else {
|
|
511
510
|
const table = opt.table || this.cfg.table;
|
|
512
|
-
|
|
511
|
+
_assert(opt.createIfMissing, `DB row required, but not found in ${table}`, {
|
|
513
512
|
id,
|
|
514
513
|
table,
|
|
515
514
|
});
|
|
@@ -543,7 +542,7 @@ class CommonDao {
|
|
|
543
542
|
...bm,
|
|
544
543
|
...patch,
|
|
545
544
|
};
|
|
546
|
-
if (
|
|
545
|
+
if (_deepJsonEquals(bm, patched)) {
|
|
547
546
|
// Skipping the save operation, as data is the same
|
|
548
547
|
return bm;
|
|
549
548
|
}
|
|
@@ -561,8 +560,8 @@ class CommonDao {
|
|
|
561
560
|
...patch,
|
|
562
561
|
};
|
|
563
562
|
// Make `bm` exactly the same as `loadedWithPatch`
|
|
564
|
-
|
|
565
|
-
if (
|
|
563
|
+
_objectAssignExact(bm, loadedWithPatch);
|
|
564
|
+
if (_deepJsonEquals(loaded, loadedWithPatch)) {
|
|
566
565
|
// Skipping the save operation, as data is the same
|
|
567
566
|
return bm;
|
|
568
567
|
}
|
|
@@ -587,14 +586,14 @@ class CommonDao {
|
|
|
587
586
|
// which should be removed post-validation, but it breaks the "equality check"
|
|
588
587
|
// Post-validation the equality check should work as intended
|
|
589
588
|
const convertedBM = this.validateAndConvert(bm, this.cfg.bmSchema, 'save', opt);
|
|
590
|
-
if (
|
|
589
|
+
if (_deepJsonEquals(convertedBM, opt.skipIfEquals)) {
|
|
591
590
|
// Skipping the save operation
|
|
592
591
|
return bm;
|
|
593
592
|
}
|
|
594
593
|
}
|
|
595
594
|
const idWasGenerated = !bm.id && this.cfg.generateId;
|
|
596
595
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
597
|
-
|
|
596
|
+
_typeCast(bm);
|
|
598
597
|
let dbm = await this.bmToDBM(bm, opt); // validates BM
|
|
599
598
|
if (this.cfg.hooks.beforeSave) {
|
|
600
599
|
dbm = (await this.cfg.hooks.beforeSave(dbm));
|
|
@@ -663,14 +662,14 @@ class CommonDao {
|
|
|
663
662
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt));
|
|
664
663
|
let dbms = await this.bmsToDBM(bms, opt);
|
|
665
664
|
if (this.cfg.hooks.beforeSave && dbms.length) {
|
|
666
|
-
dbms = (await
|
|
665
|
+
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.beforeSave(dbm))).filter(_isTruthy);
|
|
667
666
|
}
|
|
668
667
|
if (opt.ensureUniqueId)
|
|
669
|
-
throw new
|
|
668
|
+
throw new AppError('ensureUniqueId is not supported in saveBatch');
|
|
670
669
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
671
670
|
opt = { ...opt, saveMethod: 'insert' };
|
|
672
671
|
}
|
|
673
|
-
const op = `saveBatch ${dbms.length} row(s) (${
|
|
672
|
+
const op = `saveBatch ${dbms.length} row(s) (${_truncate(dbms
|
|
674
673
|
.slice(0, 10)
|
|
675
674
|
.map(bm => bm.id)
|
|
676
675
|
.join(', '), 50)})`;
|
|
@@ -696,11 +695,11 @@ class CommonDao {
|
|
|
696
695
|
dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt)); // mutates
|
|
697
696
|
let rows = this.anyToDBMs(dbms, opt);
|
|
698
697
|
if (opt.ensureUniqueId)
|
|
699
|
-
throw new
|
|
698
|
+
throw new AppError('ensureUniqueId is not supported in saveBatch');
|
|
700
699
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
701
700
|
opt = { ...opt, saveMethod: 'insert' };
|
|
702
701
|
}
|
|
703
|
-
const op = `saveBatchAsDBM ${rows.length} row(s) (${
|
|
702
|
+
const op = `saveBatchAsDBM ${rows.length} row(s) (${_truncate(rows
|
|
704
703
|
.slice(0, 10)
|
|
705
704
|
.map(bm => bm.id)
|
|
706
705
|
.join(', '), 50)})`;
|
|
@@ -708,7 +707,7 @@ class CommonDao {
|
|
|
708
707
|
const { excludeFromIndexes } = this.cfg;
|
|
709
708
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
710
709
|
if (this.cfg.hooks.beforeSave && rows.length) {
|
|
711
|
-
rows = (await
|
|
710
|
+
rows = (await pMap(rows, async (row) => await this.cfg.hooks.beforeSave(row))).filter(_isTruthy);
|
|
712
711
|
}
|
|
713
712
|
await (opt.tx || this.cfg.db).saveBatch(table, rows, {
|
|
714
713
|
excludeFromIndexes,
|
|
@@ -731,7 +730,7 @@ class CommonDao {
|
|
|
731
730
|
this.requireWriteAccess();
|
|
732
731
|
const table = opt.table || this.cfg.table;
|
|
733
732
|
opt.skipValidation ??= true;
|
|
734
|
-
opt.errorMode ||=
|
|
733
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
735
734
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
736
735
|
opt = { ...opt, saveMethod: 'insert' };
|
|
737
736
|
}
|
|
@@ -739,20 +738,20 @@ class CommonDao {
|
|
|
739
738
|
const { beforeSave } = this.cfg.hooks;
|
|
740
739
|
const { chunkSize = 500, chunkConcurrency = 32, errorMode } = opt;
|
|
741
740
|
return [
|
|
742
|
-
|
|
741
|
+
transformMap(async (bm) => {
|
|
743
742
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
744
743
|
let dbm = await this.bmToDBM(bm, opt);
|
|
745
744
|
if (beforeSave) {
|
|
746
745
|
dbm = (await beforeSave(dbm));
|
|
747
746
|
if (dbm === null)
|
|
748
|
-
return
|
|
747
|
+
return SKIP;
|
|
749
748
|
}
|
|
750
749
|
return dbm;
|
|
751
750
|
}, {
|
|
752
751
|
errorMode,
|
|
753
752
|
}),
|
|
754
|
-
|
|
755
|
-
|
|
753
|
+
transformChunk({ chunkSize }),
|
|
754
|
+
transformMap(async (batch) => {
|
|
756
755
|
await this.cfg.db.saveBatch(table, batch, {
|
|
757
756
|
...opt,
|
|
758
757
|
excludeFromIndexes,
|
|
@@ -763,13 +762,13 @@ class CommonDao {
|
|
|
763
762
|
errorMode,
|
|
764
763
|
flattenArrayOutput: true,
|
|
765
764
|
}),
|
|
766
|
-
|
|
765
|
+
transformLogProgress({
|
|
767
766
|
metric: 'saved',
|
|
768
767
|
...opt,
|
|
769
768
|
}),
|
|
770
769
|
// just to satisfy and simplify typings
|
|
771
770
|
// It's easier to return Transform[], rather than (Transform | Writable)[]
|
|
772
|
-
|
|
771
|
+
writableVoid(),
|
|
773
772
|
];
|
|
774
773
|
}
|
|
775
774
|
// DELETE
|
|
@@ -808,24 +807,24 @@ class CommonDao {
|
|
|
808
807
|
let deleted = 0;
|
|
809
808
|
if (opt.chunkSize) {
|
|
810
809
|
const { chunkSize, chunkConcurrency = 32 } = opt;
|
|
811
|
-
await
|
|
810
|
+
await _pipeline([
|
|
812
811
|
this.cfg.db.streamQuery(q.select(['id']), opt).map(r => r.id),
|
|
813
|
-
|
|
814
|
-
|
|
812
|
+
transformChunk({ chunkSize }),
|
|
813
|
+
transformMap(async (ids) => {
|
|
815
814
|
await this.cfg.db.deleteByIds(q.table, ids, opt);
|
|
816
815
|
deleted += ids.length;
|
|
817
816
|
}, {
|
|
818
|
-
predicate:
|
|
817
|
+
predicate: _passthroughPredicate,
|
|
819
818
|
concurrency: chunkConcurrency,
|
|
820
819
|
}),
|
|
821
820
|
// LogProgress should be AFTER the mapper, to be able to report correct stats
|
|
822
|
-
|
|
821
|
+
transformLogProgress({
|
|
823
822
|
metric: q.table,
|
|
824
823
|
logEvery: 2, // 500 * 2 === 1000
|
|
825
824
|
chunkSize,
|
|
826
825
|
...opt,
|
|
827
826
|
}),
|
|
828
|
-
|
|
827
|
+
writableVoid(),
|
|
829
828
|
]);
|
|
830
829
|
}
|
|
831
830
|
else {
|
|
@@ -897,7 +896,7 @@ class CommonDao {
|
|
|
897
896
|
return this.validateAndConvert(bm, this.cfg.bmSchema, 'load', opt);
|
|
898
897
|
}
|
|
899
898
|
async dbmsToBM(dbms, opt = {}) {
|
|
900
|
-
return await
|
|
899
|
+
return await pMap(dbms, async (dbm) => await this.dbmToBM(dbm, opt));
|
|
901
900
|
}
|
|
902
901
|
async bmToDBM(bm, opt) {
|
|
903
902
|
if (bm === undefined)
|
|
@@ -909,7 +908,7 @@ class CommonDao {
|
|
|
909
908
|
}
|
|
910
909
|
async bmsToDBM(bms, opt = {}) {
|
|
911
910
|
// try/catch?
|
|
912
|
-
return await
|
|
911
|
+
return await pMap(bms, async (bm) => await this.bmToDBM(bm, opt));
|
|
913
912
|
}
|
|
914
913
|
anyToDBM(dbm, opt = {}) {
|
|
915
914
|
if (!dbm)
|
|
@@ -945,7 +944,7 @@ class CommonDao {
|
|
|
945
944
|
// obj = _filterNullishValues(obj as any)
|
|
946
945
|
// We still filter `undefined` values here, because `beforeDBMToBM` can return undefined values
|
|
947
946
|
// and they can be annoying with snapshot tests
|
|
948
|
-
obj =
|
|
947
|
+
obj = _filterUndefinedValues(obj);
|
|
949
948
|
// Return as is if no schema is passed or if `skipConversion` is set
|
|
950
949
|
if (!schema ||
|
|
951
950
|
opt.skipValidation ||
|
|
@@ -958,13 +957,13 @@ class CommonDao {
|
|
|
958
957
|
const objectName = table;
|
|
959
958
|
let error;
|
|
960
959
|
let convertedValue;
|
|
961
|
-
if (schema instanceof
|
|
960
|
+
if (schema instanceof ZodSchema) {
|
|
962
961
|
// Zod schema
|
|
963
|
-
const vr =
|
|
962
|
+
const vr = zSafeValidate(obj, schema);
|
|
964
963
|
error = vr.error;
|
|
965
964
|
convertedValue = vr.data;
|
|
966
965
|
}
|
|
967
|
-
else if (schema instanceof
|
|
966
|
+
else if (schema instanceof AjvSchema) {
|
|
968
967
|
// Ajv schema
|
|
969
968
|
convertedValue = obj; // because Ajv mutates original object
|
|
970
969
|
error = schema.getValidationError(obj, {
|
|
@@ -973,9 +972,9 @@ class CommonDao {
|
|
|
973
972
|
}
|
|
974
973
|
else {
|
|
975
974
|
// Joi
|
|
976
|
-
const start =
|
|
977
|
-
const vr =
|
|
978
|
-
const tookMillis =
|
|
975
|
+
const start = localTime.nowUnixMillis();
|
|
976
|
+
const vr = getValidationResult(obj, schema, objectName);
|
|
977
|
+
const tookMillis = localTime.nowUnixMillis() - start;
|
|
979
978
|
this.cfg.onValidationTime?.({
|
|
980
979
|
tookMillis,
|
|
981
980
|
table,
|
|
@@ -1005,6 +1004,10 @@ class CommonDao {
|
|
|
1005
1004
|
async ping() {
|
|
1006
1005
|
await this.cfg.db.ping();
|
|
1007
1006
|
}
|
|
1007
|
+
async createTransaction(opt) {
|
|
1008
|
+
const tx = await this.cfg.db.createTransaction(opt);
|
|
1009
|
+
return new CommonDaoTransaction(tx, this.cfg.logger);
|
|
1010
|
+
}
|
|
1008
1011
|
async runInTransaction(fn, opt) {
|
|
1009
1012
|
let r;
|
|
1010
1013
|
await this.cfg.db.runInTransaction(async (tx) => {
|
|
@@ -1027,7 +1030,7 @@ class CommonDao {
|
|
|
1027
1030
|
if (!excludeFromIndexes)
|
|
1028
1031
|
return;
|
|
1029
1032
|
for (const f of q._filters) {
|
|
1030
|
-
|
|
1033
|
+
_assert(!excludeFromIndexes.includes(f.name), `cannot query on non-indexed property: ${this.cfg.table}.${f.name}`, {
|
|
1031
1034
|
query: q.pretty(),
|
|
1032
1035
|
});
|
|
1033
1036
|
}
|
|
@@ -1039,37 +1042,37 @@ class CommonDao {
|
|
|
1039
1042
|
const args = [];
|
|
1040
1043
|
if (Array.isArray(res)) {
|
|
1041
1044
|
logRes = `${res.length} row(s)`;
|
|
1042
|
-
if (res.length && this.cfg.logLevel >=
|
|
1045
|
+
if (res.length && this.cfg.logLevel >= CommonDaoLogLevel.DATA_FULL) {
|
|
1043
1046
|
args.push('\n', ...res.slice(0, 10)); // max 10 items
|
|
1044
1047
|
}
|
|
1045
1048
|
}
|
|
1046
1049
|
else if (res) {
|
|
1047
1050
|
logRes = `1 row`;
|
|
1048
|
-
if (this.cfg.logLevel >=
|
|
1051
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.DATA_SINGLE) {
|
|
1049
1052
|
args.push('\n', res);
|
|
1050
1053
|
}
|
|
1051
1054
|
}
|
|
1052
1055
|
else {
|
|
1053
1056
|
logRes = `undefined`;
|
|
1054
1057
|
}
|
|
1055
|
-
this.cfg.logger?.log(`<< ${table}.${op}: ${logRes} in ${
|
|
1058
|
+
this.cfg.logger?.log(`<< ${table}.${op}: ${logRes} in ${_since(started)}`, ...args);
|
|
1056
1059
|
}
|
|
1057
1060
|
logSaveResult(started, op, table) {
|
|
1058
1061
|
if (!this.cfg.logLevel)
|
|
1059
1062
|
return;
|
|
1060
|
-
this.cfg.logger?.log(`<< ${table}.${op} in ${
|
|
1063
|
+
this.cfg.logger?.log(`<< ${table}.${op} in ${_since(started)}`);
|
|
1061
1064
|
}
|
|
1062
1065
|
logStarted(op, table, force = false) {
|
|
1063
1066
|
if (this.cfg.logStarted || force) {
|
|
1064
1067
|
this.cfg.logger?.log(`>> ${table}.${op}`);
|
|
1065
1068
|
}
|
|
1066
|
-
return
|
|
1069
|
+
return localTime.nowUnixMillis();
|
|
1067
1070
|
}
|
|
1068
1071
|
logSaveStarted(op, items, table) {
|
|
1069
1072
|
if (this.cfg.logStarted) {
|
|
1070
1073
|
const args = [`>> ${table}.${op}`];
|
|
1071
1074
|
if (Array.isArray(items)) {
|
|
1072
|
-
if (items.length && this.cfg.logLevel >=
|
|
1075
|
+
if (items.length && this.cfg.logLevel >= CommonDaoLogLevel.DATA_FULL) {
|
|
1073
1076
|
args.push('\n', ...items.slice(0, 10));
|
|
1074
1077
|
}
|
|
1075
1078
|
else {
|
|
@@ -1077,27 +1080,36 @@ class CommonDao {
|
|
|
1077
1080
|
}
|
|
1078
1081
|
}
|
|
1079
1082
|
else {
|
|
1080
|
-
if (this.cfg.logLevel >=
|
|
1083
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.DATA_SINGLE) {
|
|
1081
1084
|
args.push(items);
|
|
1082
1085
|
}
|
|
1083
1086
|
}
|
|
1084
1087
|
this.cfg.logger?.log(...args);
|
|
1085
1088
|
}
|
|
1086
|
-
return
|
|
1089
|
+
return localTime.nowUnixMillis();
|
|
1087
1090
|
}
|
|
1088
1091
|
}
|
|
1089
|
-
exports.CommonDao = CommonDao;
|
|
1090
1092
|
/**
|
|
1091
1093
|
* Transaction context.
|
|
1092
1094
|
* Has similar API than CommonDao, but all operations are performed in the context of the transaction.
|
|
1093
1095
|
*/
|
|
1094
|
-
class CommonDaoTransaction {
|
|
1096
|
+
export class CommonDaoTransaction {
|
|
1097
|
+
tx;
|
|
1098
|
+
logger;
|
|
1095
1099
|
constructor(tx, logger) {
|
|
1096
1100
|
this.tx = tx;
|
|
1097
1101
|
this.logger = logger;
|
|
1098
1102
|
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Commits the underlying DBTransaction.
|
|
1105
|
+
* May throw.
|
|
1106
|
+
*/
|
|
1107
|
+
async commit() {
|
|
1108
|
+
await this.tx.commit();
|
|
1109
|
+
}
|
|
1099
1110
|
/**
|
|
1100
1111
|
* Perform a graceful rollback without throwing/re-throwing any error.
|
|
1112
|
+
* Never throws.
|
|
1101
1113
|
*/
|
|
1102
1114
|
async rollback() {
|
|
1103
1115
|
try {
|
|
@@ -1141,7 +1153,7 @@ class CommonDaoTransaction {
|
|
|
1141
1153
|
* So, this method is a rather simple convenience "Object.assign and then save".
|
|
1142
1154
|
*/
|
|
1143
1155
|
async patch(dao, bm, patch, opt) {
|
|
1144
|
-
const skipIfEquals =
|
|
1156
|
+
const skipIfEquals = _deepCopy(bm);
|
|
1145
1157
|
Object.assign(bm, patch);
|
|
1146
1158
|
return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx });
|
|
1147
1159
|
}
|
|
@@ -1154,4 +1166,3 @@ class CommonDaoTransaction {
|
|
|
1154
1166
|
return await dao.deleteByIds(ids, { ...opt, tx: this.tx });
|
|
1155
1167
|
}
|
|
1156
1168
|
}
|
|
1157
|
-
exports.CommonDaoTransaction = CommonDaoTransaction;
|