@naturalcycles/db-lib 8.46.1 → 8.48.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 +2 -3
- package/dist/adapter/cachedb/cache.db.js +12 -51
- package/dist/adapter/file/file.db.d.ts +0 -2
- package/dist/adapter/file/file.db.js +0 -20
- package/dist/adapter/inmemory/inMemory.db.d.ts +2 -3
- package/dist/adapter/inmemory/inMemory.db.js +32 -25
- package/dist/base.common.db.d.ts +8 -9
- package/dist/base.common.db.js +22 -26
- package/dist/common.db.d.ts +20 -7
- package/dist/commondao/common.dao.d.ts +4 -1
- package/dist/commondao/common.dao.js +32 -13
- package/dist/db.model.d.ts +13 -0
- package/dist/db.model.js +17 -1
- package/dist/query/dbQuery.d.ts +3 -1
- package/dist/query/dbQuery.js +8 -1
- package/dist/testing/daoTest.js +3 -4
- package/dist/testing/dbTest.d.ts +2 -0
- package/dist/testing/dbTest.js +52 -12
- package/dist/transaction/dbTransaction.util.js +5 -1
- package/package.json +1 -1
- package/src/adapter/cachedb/cache.db.ts +21 -82
- package/src/adapter/file/file.db.ts +0 -32
- package/src/adapter/inmemory/inMemory.db.ts +34 -34
- package/src/base.common.db.ts +33 -33
- package/src/common.db.ts +24 -16
- package/src/commondao/common.dao.ts +46 -15
- package/src/db.model.ts +19 -0
- package/src/query/dbQuery.ts +17 -2
- package/src/testing/daoTest.ts +13 -10
- package/src/testing/dbTest.ts +80 -17
- package/src/transaction/dbTransaction.util.ts +5 -1
package/dist/query/dbQuery.js
CHANGED
|
@@ -50,6 +50,10 @@ class DBQuery {
|
|
|
50
50
|
this._filters.push({ name, op: '==', val });
|
|
51
51
|
return this;
|
|
52
52
|
}
|
|
53
|
+
filterIn(name, val) {
|
|
54
|
+
this._filters.push({ name, op: 'in', val });
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
53
57
|
limit(limit) {
|
|
54
58
|
this._limitValue = limit;
|
|
55
59
|
return this;
|
|
@@ -86,7 +90,7 @@ class DBQuery {
|
|
|
86
90
|
return this;
|
|
87
91
|
}
|
|
88
92
|
clone() {
|
|
89
|
-
return
|
|
93
|
+
return (0, js_lib_1._objectAssign)(new DBQuery(this.table), {
|
|
90
94
|
_filters: [...this._filters],
|
|
91
95
|
_limitValue: this._limitValue,
|
|
92
96
|
_offsetValue: this._offsetValue,
|
|
@@ -164,6 +168,9 @@ class RunnableDBQuery extends DBQuery {
|
|
|
164
168
|
async runQueryCount(opt) {
|
|
165
169
|
return await this.dao.runQueryCount(this, opt);
|
|
166
170
|
}
|
|
171
|
+
async updateByQuery(patch, opt) {
|
|
172
|
+
return await this.dao.updateByQuery(this, patch, opt);
|
|
173
|
+
}
|
|
167
174
|
async streamQueryForEach(mapper, opt) {
|
|
168
175
|
await this.dao.streamQueryForEach(this, mapper, opt);
|
|
169
176
|
}
|
package/dist/testing/daoTest.js
CHANGED
|
@@ -48,7 +48,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
48
48
|
// DELETE ALL initially
|
|
49
49
|
test('deleteByIds test items', async () => {
|
|
50
50
|
const rows = await dao.query().select(['id']).runQuery();
|
|
51
|
-
await db.
|
|
51
|
+
await db.deleteByQuery(__1.DBQuery.create(test_model_1.TEST_TABLE).filter('id', 'in', rows.map(r => r.id)));
|
|
52
52
|
});
|
|
53
53
|
// QUERY empty
|
|
54
54
|
test('runQuery(all), runQueryCount should return empty', async () => {
|
|
@@ -114,7 +114,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
114
114
|
// GET not empty
|
|
115
115
|
test('getByIds all items', async () => {
|
|
116
116
|
const rows = await dao.getByIds(items.map(i => i.id).concat('abcd'));
|
|
117
|
-
(0, dbTest_1.expectMatch)(expectedItems, rows, quirks);
|
|
117
|
+
(0, dbTest_1.expectMatch)(expectedItems, (0, js_lib_1._sortBy)(rows, r => r.id), quirks);
|
|
118
118
|
});
|
|
119
119
|
// QUERY
|
|
120
120
|
if (querying) {
|
|
@@ -187,8 +187,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
187
187
|
});
|
|
188
188
|
test('cleanup', async () => {
|
|
189
189
|
// CLEAN UP
|
|
190
|
-
|
|
191
|
-
await db.deleteByIds(test_model_1.TEST_TABLE, rows.map(i => i.id));
|
|
190
|
+
await dao.query().deleteByQuery();
|
|
192
191
|
});
|
|
193
192
|
}
|
|
194
193
|
if (transactions) {
|
package/dist/testing/dbTest.d.ts
CHANGED
package/dist/testing/dbTest.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.expectMatch = exports.runCommonDBTest = void 0;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
6
|
+
const db_model_1 = require("../db.model");
|
|
6
7
|
const dbQuery_1 = require("../query/dbQuery");
|
|
7
8
|
const dbTransaction_1 = require("../transaction/dbTransaction");
|
|
8
9
|
const test_model_1 = require("./test.model");
|
|
@@ -13,7 +14,7 @@ const test_util_1 = require("./test.util");
|
|
|
13
14
|
function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
14
15
|
const { querying = true, tableSchemas = true, createTable = true, dbQueryFilter = true,
|
|
15
16
|
// dbQueryFilterIn = true,
|
|
16
|
-
dbQueryOrder = true, dbQuerySelectFields = true, insert = true, update = true, streaming = true, strongConsistency = true, bufferSupport = true, nullValues = true, documentDB = true, transactions = true, } = features;
|
|
17
|
+
dbQueryOrder = true, dbQuerySelectFields = true, insert = true, update = true, updateByQuery = true, dbIncrement = true, streaming = true, strongConsistency = true, bufferSupport = true, nullValues = true, documentDB = true, transactions = true, } = features;
|
|
17
18
|
// const {
|
|
18
19
|
// allowExtraPropertiesInResponse,
|
|
19
20
|
// allowBooleansAsUndefined,
|
|
@@ -36,7 +37,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
36
37
|
// DELETE ALL initially
|
|
37
38
|
test('deleteByIds test items', async () => {
|
|
38
39
|
const { rows } = await db.runQuery(queryAll().select(['id']));
|
|
39
|
-
await db.
|
|
40
|
+
await db.runQuery(queryAll().filter('id', 'in', rows.map(i => i.id)));
|
|
40
41
|
});
|
|
41
42
|
// QUERY empty
|
|
42
43
|
test('runQuery(all), runQueryCount should return empty', async () => {
|
|
@@ -48,15 +49,15 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
48
49
|
}
|
|
49
50
|
// GET empty
|
|
50
51
|
test('getByIds(item1.id) should return empty', async () => {
|
|
51
|
-
const [item1Loaded] = await db.
|
|
52
|
+
const [item1Loaded] = (await db.runQuery(queryAll().filterEq('id', item1.id))).rows;
|
|
52
53
|
// console.log(a)
|
|
53
54
|
expect(item1Loaded).toBeUndefined();
|
|
54
55
|
});
|
|
55
56
|
test('getByIds([]) should return []', async () => {
|
|
56
|
-
expect(await db.
|
|
57
|
+
expect((await db.runQuery(queryAll().filter('id', 'in', []))).rows).toEqual([]);
|
|
57
58
|
});
|
|
58
59
|
test('getByIds(...) should return empty', async () => {
|
|
59
|
-
expect(await db.
|
|
60
|
+
expect((await db.runQuery(queryAll().filter('id', 'in', ['abc', 'abcd']))).rows).toEqual([]);
|
|
60
61
|
});
|
|
61
62
|
// SAVE
|
|
62
63
|
if (nullValues) {
|
|
@@ -67,7 +68,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
67
68
|
};
|
|
68
69
|
(0, test_util_1.deepFreeze)(item3);
|
|
69
70
|
await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
|
|
70
|
-
const item3Loaded = (await db.
|
|
71
|
+
const item3Loaded = (await db.runQuery(queryAll().filterEq('id', item3.id))).rows[0];
|
|
71
72
|
expectMatch([item3], [item3Loaded], quirks);
|
|
72
73
|
expect(item3Loaded.k2).toBeNull();
|
|
73
74
|
});
|
|
@@ -82,7 +83,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
82
83
|
const expected = { ...item3 };
|
|
83
84
|
delete expected.k2;
|
|
84
85
|
await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
|
|
85
|
-
const item3Loaded = (await db.
|
|
86
|
+
const item3Loaded = (await db.runQuery(queryAll().filterEq('id', item3.id))).rows[0];
|
|
86
87
|
expectMatch([expected], [item3Loaded], quirks);
|
|
87
88
|
expect(item3Loaded.k2).toBeUndefined();
|
|
88
89
|
expect(Object.keys(item3Loaded)).not.toContain('k2');
|
|
@@ -111,8 +112,8 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
111
112
|
}
|
|
112
113
|
// GET not empty
|
|
113
114
|
test('getByIds all items', async () => {
|
|
114
|
-
const rows = await db.
|
|
115
|
-
expectMatch(items, rows, quirks);
|
|
115
|
+
const rows = (await db.runQuery(queryAll().filter('id', 'in', items.map(i => i.id).concat('abcd')))).rows;
|
|
116
|
+
expectMatch(items, (0, js_lib_1._sortBy)(rows, r => r.id), quirks);
|
|
116
117
|
});
|
|
117
118
|
// QUERY
|
|
118
119
|
if (querying) {
|
|
@@ -203,7 +204,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
203
204
|
b1,
|
|
204
205
|
};
|
|
205
206
|
await db.saveBatch(test_model_1.TEST_TABLE, [item]);
|
|
206
|
-
const
|
|
207
|
+
const loaded = (await db.runQuery(queryAll().filterEq('id', item.id))).rows[0];
|
|
207
208
|
const b1Loaded = loaded.b1;
|
|
208
209
|
// console.log({
|
|
209
210
|
// b11: typeof b1,
|
|
@@ -245,11 +246,50 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
|
|
|
245
246
|
expectMatch(expected, rows, quirks);
|
|
246
247
|
});
|
|
247
248
|
}
|
|
249
|
+
if (updateByQuery) {
|
|
250
|
+
test('updateByQuery simple', async () => {
|
|
251
|
+
// cleanup, reset initial data
|
|
252
|
+
await db.deleteByQuery(queryAll());
|
|
253
|
+
await db.saveBatch(test_model_1.TEST_TABLE, items);
|
|
254
|
+
const patch = {
|
|
255
|
+
k3: 5,
|
|
256
|
+
k2: 'abc',
|
|
257
|
+
};
|
|
258
|
+
await db.updateByQuery(dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE).filterEq('even', true), patch);
|
|
259
|
+
const { rows } = await db.runQuery(queryAll());
|
|
260
|
+
const expected = items.map(r => {
|
|
261
|
+
if (r.even) {
|
|
262
|
+
return { ...r, ...patch };
|
|
263
|
+
}
|
|
264
|
+
return r;
|
|
265
|
+
});
|
|
266
|
+
expectMatch(expected, rows, quirks);
|
|
267
|
+
});
|
|
268
|
+
if (dbIncrement) {
|
|
269
|
+
test('updateByQuery DBIncrement', async () => {
|
|
270
|
+
// cleanup, reset initial data
|
|
271
|
+
await db.deleteByQuery(queryAll());
|
|
272
|
+
await db.saveBatch(test_model_1.TEST_TABLE, items);
|
|
273
|
+
const patch = {
|
|
274
|
+
k3: db_model_1.DBIncrement.of(1),
|
|
275
|
+
k2: 'abcd',
|
|
276
|
+
};
|
|
277
|
+
await db.updateByQuery(dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE).filterEq('even', true), patch);
|
|
278
|
+
const { rows } = await db.runQuery(queryAll());
|
|
279
|
+
const expected = items.map(r => {
|
|
280
|
+
if (r.even) {
|
|
281
|
+
return { ...r, ...patch, k3: (r.k3 || 0) + 1 };
|
|
282
|
+
}
|
|
283
|
+
return r;
|
|
284
|
+
});
|
|
285
|
+
expectMatch(expected, rows, quirks);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
248
289
|
if (querying) {
|
|
249
290
|
test('cleanup', async () => {
|
|
250
291
|
// CLEAN UP
|
|
251
|
-
|
|
252
|
-
await db.deleteByIds(test_model_1.TEST_TABLE, rows.map(i => i.id));
|
|
292
|
+
await db.deleteByQuery(queryAll());
|
|
253
293
|
});
|
|
254
294
|
}
|
|
255
295
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.commitDBTransactionSimple = exports.mergeDBOperations = void 0;
|
|
4
|
+
const dbQuery_1 = require("../query/dbQuery");
|
|
4
5
|
/**
|
|
5
6
|
* Optimizes the Transaction (list of DBOperations) to do less operations.
|
|
6
7
|
* E.g if you save id1 first and then delete it - this function will turn it into a no-op (self-eliminate).
|
|
@@ -74,7 +75,10 @@ async function commitDBTransactionSimple(db, tx, opt) {
|
|
|
74
75
|
await db.saveBatch(op.table, op.rows, { ...op.opt, ...opt });
|
|
75
76
|
}
|
|
76
77
|
else if (op.type === 'deleteByIds') {
|
|
77
|
-
await db.
|
|
78
|
+
await db.deleteByQuery(dbQuery_1.DBQuery.create(op.table).filter('id', 'in', op.ids), {
|
|
79
|
+
...op.opt,
|
|
80
|
+
...opt,
|
|
81
|
+
});
|
|
78
82
|
}
|
|
79
83
|
else {
|
|
80
84
|
throw new Error(`DBOperation not supported: ${op.type}`);
|
package/package.json
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { Readable } from 'node:stream'
|
|
2
|
-
import {
|
|
3
|
-
JsonSchemaObject,
|
|
4
|
-
JsonSchemaRootObject,
|
|
5
|
-
ObjectWithId,
|
|
6
|
-
StringMap,
|
|
7
|
-
} from '@naturalcycles/js-lib'
|
|
2
|
+
import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib'
|
|
8
3
|
import { BaseCommonDB } from '../../base.common.db'
|
|
9
4
|
import { CommonDB } from '../../common.db'
|
|
10
|
-
import { CommonDBOptions, RunQueryResult } from '../../db.model'
|
|
5
|
+
import { CommonDBOptions, DBPatch, RunQueryResult } from '../../db.model'
|
|
11
6
|
import { DBQuery } from '../../query/dbQuery'
|
|
12
7
|
import { DBTransaction } from '../../transaction/dbTransaction'
|
|
13
8
|
import {
|
|
@@ -72,81 +67,6 @@ export class CacheDB extends BaseCommonDB implements CommonDB {
|
|
|
72
67
|
}
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
override async getByIds<ROW extends ObjectWithId>(
|
|
76
|
-
table: string,
|
|
77
|
-
ids: ROW['id'][],
|
|
78
|
-
opt: CacheDBSaveOptions<ROW> = {},
|
|
79
|
-
): Promise<ROW[]> {
|
|
80
|
-
const resultMap: StringMap<ROW> = {}
|
|
81
|
-
const missingIds: ROW['id'][] = []
|
|
82
|
-
|
|
83
|
-
if (!opt.skipCache && !this.cfg.skipCache) {
|
|
84
|
-
const results = await this.cfg.cacheDB.getByIds<ROW>(table, ids, opt)
|
|
85
|
-
|
|
86
|
-
results.forEach(r => (resultMap[r.id] = r))
|
|
87
|
-
|
|
88
|
-
missingIds.push(...ids.filter(id => !resultMap[id]))
|
|
89
|
-
|
|
90
|
-
if (this.cfg.logCached) {
|
|
91
|
-
this.cfg.logger?.log(
|
|
92
|
-
`${table}.getByIds ${results.length} rows from cache: [${results
|
|
93
|
-
.map(r => r.id)
|
|
94
|
-
.join(', ')}]`,
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (missingIds.length && !opt.onlyCache && !this.cfg.onlyCache) {
|
|
100
|
-
const results = await this.cfg.downstreamDB.getByIds<ROW>(table, missingIds, opt)
|
|
101
|
-
results.forEach(r => (resultMap[r.id] = r))
|
|
102
|
-
|
|
103
|
-
if (this.cfg.logDownstream) {
|
|
104
|
-
this.cfg.logger?.log(
|
|
105
|
-
`${table}.getByIds ${results.length} rows from downstream: [${results
|
|
106
|
-
.map(r => r.id)
|
|
107
|
-
.join(', ')}]`,
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!opt.skipCache) {
|
|
112
|
-
const cacheResult = this.cfg.cacheDB.saveBatch(table, results, opt)
|
|
113
|
-
if (this.cfg.awaitCache) await cacheResult
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// return in right order
|
|
118
|
-
return ids.map(id => resultMap[id]!).filter(Boolean)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
override async deleteByIds<ROW extends ObjectWithId>(
|
|
122
|
-
table: string,
|
|
123
|
-
ids: ROW['id'][],
|
|
124
|
-
opt: CacheDBOptions = {},
|
|
125
|
-
): Promise<number> {
|
|
126
|
-
let deletedIds = 0
|
|
127
|
-
|
|
128
|
-
if (!opt.onlyCache && !this.cfg.onlyCache) {
|
|
129
|
-
deletedIds = await this.cfg.downstreamDB.deleteByIds<ROW>(table, ids, opt)
|
|
130
|
-
|
|
131
|
-
if (this.cfg.logDownstream) {
|
|
132
|
-
this.cfg.logger?.log(`${table}.deleteByIds ${deletedIds} rows from downstream`)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (!opt.skipCache && !this.cfg.skipCache) {
|
|
137
|
-
const cacheResult = this.cfg.cacheDB
|
|
138
|
-
.deleteByIds<ROW>(table, ids, opt)
|
|
139
|
-
.then(deletedFromCache => {
|
|
140
|
-
if (this.cfg.logCached) {
|
|
141
|
-
this.cfg.logger?.log(`${table}.deleteByIds ${deletedFromCache} rows from cache`)
|
|
142
|
-
}
|
|
143
|
-
})
|
|
144
|
-
if (this.cfg.awaitCache) await cacheResult
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return deletedIds
|
|
148
|
-
}
|
|
149
|
-
|
|
150
70
|
override async saveBatch<ROW extends Partial<ObjectWithId>>(
|
|
151
71
|
table: string,
|
|
152
72
|
rows: ROW[],
|
|
@@ -294,6 +214,25 @@ export class CacheDB extends BaseCommonDB implements CommonDB {
|
|
|
294
214
|
return deletedIds
|
|
295
215
|
}
|
|
296
216
|
|
|
217
|
+
override async updateByQuery<ROW extends ObjectWithId>(
|
|
218
|
+
q: DBQuery<ROW>,
|
|
219
|
+
patch: DBPatch<ROW>,
|
|
220
|
+
opt: CacheDBOptions = {},
|
|
221
|
+
): Promise<number> {
|
|
222
|
+
let updated: number | undefined
|
|
223
|
+
|
|
224
|
+
if (!opt.onlyCache && !this.cfg.onlyCache) {
|
|
225
|
+
updated = await this.cfg.downstreamDB.updateByQuery(q, patch, opt)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!opt.skipCache && !this.cfg.skipCache) {
|
|
229
|
+
const cacheResult = this.cfg.cacheDB.updateByQuery(q, patch, opt)
|
|
230
|
+
if (this.cfg.awaitCache) updated ??= await cacheResult
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return updated || 0
|
|
234
|
+
}
|
|
235
|
+
|
|
297
236
|
override async commitTransaction(tx: DBTransaction, opt?: CommonDBOptions): Promise<void> {
|
|
298
237
|
await this.cfg.downstreamDB.commitTransaction(tx, opt)
|
|
299
238
|
await this.cfg.cacheDB.commitTransaction(tx, opt)
|
|
@@ -65,15 +65,6 @@ export class FileDB extends BaseCommonDB implements CommonDB {
|
|
|
65
65
|
return tables
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
override async getByIds<ROW extends ObjectWithId>(
|
|
69
|
-
table: string,
|
|
70
|
-
ids: ROW['id'][],
|
|
71
|
-
_opt?: CommonDBOptions,
|
|
72
|
-
): Promise<ROW[]> {
|
|
73
|
-
const byId = _by(await this.loadFile<ROW>(table), r => r.id)
|
|
74
|
-
return ids.map(id => byId[id]!).filter(Boolean)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
68
|
override async saveBatch<ROW extends Partial<ObjectWithId>>(
|
|
78
69
|
table: string,
|
|
79
70
|
rows: ROW[],
|
|
@@ -198,29 +189,6 @@ export class FileDB extends BaseCommonDB implements CommonDB {
|
|
|
198
189
|
return readable
|
|
199
190
|
}
|
|
200
191
|
|
|
201
|
-
override async deleteByIds<ROW extends ObjectWithId>(
|
|
202
|
-
table: string,
|
|
203
|
-
ids: ROW['id'][],
|
|
204
|
-
_opt?: CommonDBOptions,
|
|
205
|
-
): Promise<number> {
|
|
206
|
-
if (!ids.length) return 0
|
|
207
|
-
|
|
208
|
-
let deleted = 0
|
|
209
|
-
const rows = (await this.loadFile<ROW>(table)).filter(r => {
|
|
210
|
-
if (ids.includes(r.id)) {
|
|
211
|
-
deleted++
|
|
212
|
-
return false
|
|
213
|
-
}
|
|
214
|
-
return true
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
if (deleted > 0) {
|
|
218
|
-
await this.saveFile(table, rows)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return deleted
|
|
222
|
-
}
|
|
223
|
-
|
|
224
192
|
override async deleteByQuery<ROW extends ObjectWithId>(
|
|
225
193
|
q: DBQuery<ROW>,
|
|
226
194
|
_opt?: CommonDBOptions,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
_stringMapValues,
|
|
14
14
|
CommonLogger,
|
|
15
15
|
_deepCopy,
|
|
16
|
+
_assert,
|
|
16
17
|
} from '@naturalcycles/js-lib'
|
|
17
18
|
import {
|
|
18
19
|
bufferReviver,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
} from '@naturalcycles/nodejs-lib'
|
|
26
27
|
import { dimGrey, yellow } from '@naturalcycles/nodejs-lib/dist/colors'
|
|
27
28
|
import * as fs from 'fs-extra'
|
|
28
|
-
import { CommonDB, DBTransaction, queryInMemory } from '../..'
|
|
29
|
+
import { CommonDB, DBIncrement, DBPatch, DBTransaction, queryInMemory } from '../..'
|
|
29
30
|
import {
|
|
30
31
|
CommonDBCreateOptions,
|
|
31
32
|
CommonDBOptions,
|
|
@@ -142,16 +143,6 @@ export class InMemoryDB implements CommonDB {
|
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
async getByIds<ROW extends ObjectWithId>(
|
|
146
|
-
_table: string,
|
|
147
|
-
ids: ROW['id'][],
|
|
148
|
-
_opt?: CommonDBOptions,
|
|
149
|
-
): Promise<ROW[]> {
|
|
150
|
-
const table = this.cfg.tablesPrefix + _table
|
|
151
|
-
this.data[table] ||= {}
|
|
152
|
-
return ids.map(id => this.data[table]![id]).filter(Boolean) as ROW[]
|
|
153
|
-
}
|
|
154
|
-
|
|
155
146
|
async saveBatch<ROW extends Partial<ObjectWithId>>(
|
|
156
147
|
_table: string,
|
|
157
148
|
rows: ROW[],
|
|
@@ -183,31 +174,41 @@ export class InMemoryDB implements CommonDB {
|
|
|
183
174
|
})
|
|
184
175
|
}
|
|
185
176
|
|
|
186
|
-
async
|
|
187
|
-
|
|
188
|
-
ids: ROW['id'][],
|
|
177
|
+
async deleteByQuery<ROW extends ObjectWithId>(
|
|
178
|
+
q: DBQuery<ROW>,
|
|
189
179
|
_opt?: CommonDBOptions,
|
|
190
180
|
): Promise<number> {
|
|
191
|
-
const table = this.cfg.tablesPrefix +
|
|
181
|
+
const table = this.cfg.tablesPrefix + q.table
|
|
192
182
|
this.data[table] ||= {}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
.filter(Boolean).length
|
|
183
|
+
let count = 0
|
|
184
|
+
queryInMemory(q, Object.values(this.data[table] || {}) as ROW[]).forEach(r => {
|
|
185
|
+
if (!this.data[table]![r.id]) return
|
|
186
|
+
delete this.data[table]![r.id]
|
|
187
|
+
count++
|
|
188
|
+
})
|
|
189
|
+
return count
|
|
201
190
|
}
|
|
202
191
|
|
|
203
|
-
async
|
|
192
|
+
async updateByQuery<ROW extends ObjectWithId>(
|
|
204
193
|
q: DBQuery<ROW>,
|
|
205
|
-
|
|
194
|
+
patch: DBPatch<ROW>,
|
|
206
195
|
): Promise<number> {
|
|
196
|
+
const patchEntries = Object.entries(patch)
|
|
197
|
+
if (!patchEntries.length) return 0
|
|
198
|
+
|
|
207
199
|
const table = this.cfg.tablesPrefix + q.table
|
|
208
200
|
const rows = queryInMemory(q, Object.values(this.data[table] || {}) as ROW[])
|
|
209
|
-
|
|
210
|
-
|
|
201
|
+
rows.forEach((row: any) => {
|
|
202
|
+
patchEntries.forEach(([k, v]) => {
|
|
203
|
+
if (v instanceof DBIncrement) {
|
|
204
|
+
row[k] = (row[k] || 0) + v.amount
|
|
205
|
+
} else {
|
|
206
|
+
row[k] = v
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
return rows.length
|
|
211
212
|
}
|
|
212
213
|
|
|
213
214
|
async runQuery<ROW extends ObjectWithId>(
|
|
@@ -242,7 +243,10 @@ export class InMemoryDB implements CommonDB {
|
|
|
242
243
|
if (op.type === 'saveBatch') {
|
|
243
244
|
await this.saveBatch(op.table, op.rows, { ...op.opt, ...opt })
|
|
244
245
|
} else if (op.type === 'deleteByIds') {
|
|
245
|
-
await this.
|
|
246
|
+
await this.deleteByQuery(DBQuery.create(op.table).filter('id', 'in', op.ids), {
|
|
247
|
+
...op.opt,
|
|
248
|
+
...opt,
|
|
249
|
+
})
|
|
246
250
|
} else {
|
|
247
251
|
throw new Error(`DBOperation not supported: ${(op as any).type}`)
|
|
248
252
|
}
|
|
@@ -260,9 +264,7 @@ export class InMemoryDB implements CommonDB {
|
|
|
260
264
|
* Flushes all tables (all namespaces) at once.
|
|
261
265
|
*/
|
|
262
266
|
async flushToDisk(): Promise<void> {
|
|
263
|
-
|
|
264
|
-
throw new Error('flushToDisk() called but persistenceEnabled=false')
|
|
265
|
-
}
|
|
267
|
+
_assert(this.cfg.persistenceEnabled, 'flushToDisk() called but persistenceEnabled=false')
|
|
266
268
|
const { persistentStoragePath, persistZip } = this.cfg
|
|
267
269
|
|
|
268
270
|
const started = Date.now()
|
|
@@ -297,9 +299,7 @@ export class InMemoryDB implements CommonDB {
|
|
|
297
299
|
* Restores all tables (all namespaces) at once.
|
|
298
300
|
*/
|
|
299
301
|
async restoreFromDisk(): Promise<void> {
|
|
300
|
-
|
|
301
|
-
throw new Error('restoreFromDisk() called but persistenceEnabled=false')
|
|
302
|
-
}
|
|
302
|
+
_assert(this.cfg.persistenceEnabled, 'restoreFromDisk() called but persistenceEnabled=false')
|
|
303
303
|
const { persistentStoragePath } = this.cfg
|
|
304
304
|
|
|
305
305
|
const started = Date.now()
|
package/src/base.common.db.ts
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { Readable } from 'node:stream'
|
|
2
1
|
import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib'
|
|
3
2
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
4
3
|
import { CommonDB } from './common.db'
|
|
5
|
-
import { CommonDBOptions, CommonDBSaveOptions, RunQueryResult } from './db.model'
|
|
4
|
+
import { CommonDBOptions, CommonDBSaveOptions, DBPatch, RunQueryResult } from './db.model'
|
|
6
5
|
import { DBQuery } from './query/dbQuery'
|
|
7
6
|
import { DBTransaction } from './transaction/dbTransaction'
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
/* eslint-disable unused-imports/no-unused-vars */
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* No-op implementation of CommonDB interface.
|
|
12
12
|
* To be extended by actual implementations.
|
|
13
13
|
*/
|
|
14
14
|
export class BaseCommonDB implements CommonDB {
|
|
15
|
-
async ping(): Promise<void> {
|
|
15
|
+
async ping(): Promise<void> {
|
|
16
|
+
throw new Error('ping is not implemented')
|
|
17
|
+
}
|
|
16
18
|
|
|
17
19
|
async getTables(): Promise<string[]> {
|
|
18
|
-
|
|
20
|
+
throw new Error('getTables is not implemented')
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
async getTableSchema<ROW extends ObjectWithId>(
|
|
22
24
|
table: string,
|
|
23
25
|
): Promise<JsonSchemaRootObject<ROW>> {
|
|
24
|
-
|
|
25
|
-
$id: `${table}.schema.json`,
|
|
26
|
-
type: 'object',
|
|
27
|
-
additionalProperties: true,
|
|
28
|
-
properties: {} as any,
|
|
29
|
-
required: [],
|
|
30
|
-
}
|
|
26
|
+
throw new Error('getTableSchema is not implemented')
|
|
31
27
|
}
|
|
32
28
|
|
|
33
29
|
async createTable<ROW extends ObjectWithId>(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
): Promise<void> {
|
|
37
|
-
|
|
38
|
-
async deleteByIds<ROW extends ObjectWithId>(_table: string, _ids: ROW['id'][]): Promise<number> {
|
|
39
|
-
return 0
|
|
30
|
+
table: string,
|
|
31
|
+
schema: JsonSchemaObject<ROW>,
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
// no-op
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
async deleteByQuery<ROW extends ObjectWithId>(
|
|
43
|
-
|
|
36
|
+
async deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number> {
|
|
37
|
+
throw new Error('deleteByQuery is not implemented')
|
|
44
38
|
}
|
|
45
39
|
|
|
46
|
-
async
|
|
47
|
-
|
|
40
|
+
async updateByQuery<ROW extends ObjectWithId>(
|
|
41
|
+
q: DBQuery<ROW>,
|
|
42
|
+
patch: DBPatch<ROW>,
|
|
43
|
+
opt?: CommonDBOptions,
|
|
44
|
+
): Promise<number> {
|
|
45
|
+
throw new Error('updateByQuery is not implemented')
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
async runQuery<ROW extends ObjectWithId>(
|
|
51
|
-
|
|
48
|
+
async runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<RunQueryResult<ROW>> {
|
|
49
|
+
throw new Error('runQuery is not implemented')
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
async runQueryCount<ROW extends ObjectWithId>(
|
|
55
|
-
|
|
52
|
+
async runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number> {
|
|
53
|
+
throw new Error('runQueryCount is not implemented')
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
async saveBatch<ROW extends Partial<ObjectWithId>>(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
): Promise<void> {
|
|
57
|
+
table: string,
|
|
58
|
+
rows: ROW[],
|
|
59
|
+
opt?: CommonDBSaveOptions<ROW>,
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
throw new Error('saveBatch is not implemented')
|
|
62
|
+
}
|
|
63
63
|
|
|
64
|
-
streamQuery<ROW extends ObjectWithId>(
|
|
65
|
-
|
|
64
|
+
streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): ReadableTyped<ROW> {
|
|
65
|
+
throw new Error('streamQuery is not implemented')
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
@@ -71,6 +71,6 @@ export class BaseCommonDB implements CommonDB {
|
|
|
71
71
|
* To be extended.
|
|
72
72
|
*/
|
|
73
73
|
async commitTransaction(tx: DBTransaction, opt?: CommonDBOptions): Promise<void> {
|
|
74
|
-
|
|
74
|
+
throw new Error('commitTransaction is not implemented')
|
|
75
75
|
}
|
|
76
76
|
}
|