@naturalcycles/db-lib 9.21.0 → 9.22.1
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 -2
- package/dist/adapter/cachedb/cache.db.js +4 -3
- package/dist/adapter/file/file.db.js +2 -1
- package/dist/adapter/inmemory/inMemory.db.d.ts +7 -6
- package/dist/adapter/inmemory/inMemory.db.js +15 -13
- package/dist/adapter/inmemory/inMemoryKeyValueDB.d.ts +8 -8
- package/dist/adapter/inmemory/inMemoryKeyValueDB.js +8 -7
- package/dist/base.common.db.d.ts +4 -3
- package/dist/base.common.db.js +5 -2
- package/dist/common.db.d.ts +23 -14
- package/dist/common.db.js +2 -2
- package/dist/commondao/common.dao.d.ts +16 -5
- package/dist/commondao/common.dao.js +37 -8
- package/dist/db.model.d.ts +0 -13
- package/dist/db.model.js +1 -17
- package/dist/kv/commonKeyValueDB.d.ts +18 -10
- package/dist/kv/commonKeyValueDao.d.ts +17 -25
- package/dist/kv/commonKeyValueDao.js +32 -54
- package/dist/kv/commonKeyValueDaoMemoCache.d.ts +2 -2
- package/dist/query/dbQuery.d.ts +2 -2
- package/dist/query/dbQuery.js +2 -2
- package/dist/testing/daoTest.js +26 -0
- package/dist/testing/dbTest.js +21 -23
- package/dist/testing/keyValueDBTest.js +22 -7
- package/dist/testing/keyValueDaoTest.d.ts +2 -2
- package/dist/testing/keyValueDaoTest.js +31 -8
- package/package.json +1 -1
- package/src/adapter/cachedb/cache.db.ts +6 -5
- package/src/adapter/file/file.db.ts +2 -1
- package/src/adapter/inmemory/inMemory.db.ts +29 -18
- package/src/adapter/inmemory/inMemoryKeyValueDB.ts +16 -16
- package/src/base.common.db.ts +18 -5
- package/src/common.db.ts +36 -17
- package/src/commondao/common.dao.ts +46 -11
- package/src/db.model.ts +0 -19
- package/src/kv/commonKeyValueDB.ts +19 -11
- package/src/kv/commonKeyValueDao.ts +53 -86
- package/src/kv/commonKeyValueDaoMemoCache.ts +2 -2
- package/src/query/dbQuery.ts +2 -3
- package/src/testing/daoTest.ts +28 -0
- package/src/testing/dbTest.ts +22 -28
- package/src/testing/keyValueDBTest.ts +31 -14
- package/src/testing/keyValueDaoTest.ts +37 -12
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CommonKeyValueDao = void 0;
|
|
3
|
+
exports.CommonKeyValueDao = exports.commonKeyValueDaoDeflatedJsonTransformer = void 0;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
6
|
+
exports.commonKeyValueDaoDeflatedJsonTransformer = {
|
|
7
|
+
valueToRaw: async (v) => await (0, nodejs_lib_1.deflateString)(JSON.stringify(v)),
|
|
8
|
+
rawToValue: async (raw) => JSON.parse(await (0, nodejs_lib_1.inflateToString)(raw)),
|
|
9
|
+
};
|
|
6
10
|
// todo: logging
|
|
7
11
|
// todo: readonly
|
|
8
12
|
class CommonKeyValueDao {
|
|
9
13
|
constructor(cfg) {
|
|
10
14
|
this.cfg = {
|
|
11
|
-
hooks: {},
|
|
12
15
|
logger: console,
|
|
13
16
|
...cfg,
|
|
14
17
|
};
|
|
15
|
-
if (cfg.deflatedJsonValue) {
|
|
16
|
-
this.cfg.hooks = {
|
|
17
|
-
mapValueToBuffer: async (v) => await (0, nodejs_lib_1.deflateString)(JSON.stringify(v)),
|
|
18
|
-
mapBufferToValue: async (buf) => JSON.parse(await (0, nodejs_lib_1.inflateToString)(buf)),
|
|
19
|
-
...cfg.hooks,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
18
|
}
|
|
23
19
|
async ping() {
|
|
24
20
|
await this.cfg.db.ping();
|
|
@@ -26,18 +22,13 @@ class CommonKeyValueDao {
|
|
|
26
22
|
async createTable(opt = {}) {
|
|
27
23
|
await this.cfg.db.createTable(this.cfg.table, opt);
|
|
28
24
|
}
|
|
29
|
-
create(input = {}) {
|
|
30
|
-
return {
|
|
31
|
-
...this.cfg.hooks.beforeCreate?.(input),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
25
|
async getById(id) {
|
|
35
26
|
if (!id)
|
|
36
27
|
return null;
|
|
37
28
|
const [r] = await this.getByIds([id]);
|
|
38
29
|
return r?.[1] || null;
|
|
39
30
|
}
|
|
40
|
-
async
|
|
31
|
+
async getByIdRaw(id) {
|
|
41
32
|
if (!id)
|
|
42
33
|
return null;
|
|
43
34
|
const [r] = await this.cfg.db.getByIds(this.cfg.table, [id]);
|
|
@@ -54,7 +45,7 @@ class CommonKeyValueDao {
|
|
|
54
45
|
}
|
|
55
46
|
return r[1];
|
|
56
47
|
}
|
|
57
|
-
async
|
|
48
|
+
async requireByIdRaw(id) {
|
|
58
49
|
const [r] = await this.cfg.db.getByIds(this.cfg.table, [id]);
|
|
59
50
|
if (!r) {
|
|
60
51
|
const { table } = this.cfg;
|
|
@@ -65,53 +56,36 @@ class CommonKeyValueDao {
|
|
|
65
56
|
}
|
|
66
57
|
return r[1];
|
|
67
58
|
}
|
|
68
|
-
async getByIdOrEmpty(id, part = {}) {
|
|
69
|
-
const [r] = await this.getByIds([id]);
|
|
70
|
-
if (r)
|
|
71
|
-
return r[1];
|
|
72
|
-
return {
|
|
73
|
-
...this.cfg.hooks.beforeCreate?.({}),
|
|
74
|
-
...part,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
async patch(id, patch, opt) {
|
|
78
|
-
const v = {
|
|
79
|
-
...(await this.getByIdOrEmpty(id)),
|
|
80
|
-
...patch,
|
|
81
|
-
};
|
|
82
|
-
await this.save(id, v, opt);
|
|
83
|
-
return v;
|
|
84
|
-
}
|
|
85
59
|
async getByIds(ids) {
|
|
86
60
|
const entries = await this.cfg.db.getByIds(this.cfg.table, ids);
|
|
87
|
-
if (!this.cfg.
|
|
61
|
+
if (!this.cfg.transformer)
|
|
88
62
|
return entries;
|
|
89
|
-
return await (0, js_lib_1.pMap)(entries, async ([id,
|
|
63
|
+
return await (0, js_lib_1.pMap)(entries, async ([id, raw]) => [
|
|
90
64
|
id,
|
|
91
|
-
await this.cfg.
|
|
65
|
+
await this.cfg.transformer.rawToValue(raw),
|
|
92
66
|
]);
|
|
93
67
|
}
|
|
94
|
-
async
|
|
68
|
+
async getByIdsRaw(ids) {
|
|
95
69
|
return (await this.cfg.db.getByIds(this.cfg.table, ids));
|
|
96
70
|
}
|
|
97
71
|
async save(id, value, opt) {
|
|
98
72
|
await this.saveBatch([[id, value]], opt);
|
|
99
73
|
}
|
|
100
|
-
async
|
|
74
|
+
async saveRaw(id, value, opt) {
|
|
101
75
|
await this.cfg.db.saveBatch(this.cfg.table, [[id, value]], opt);
|
|
102
76
|
}
|
|
103
77
|
async saveBatch(entries, opt) {
|
|
104
|
-
const {
|
|
105
|
-
let
|
|
106
|
-
if (!
|
|
107
|
-
|
|
78
|
+
const { transformer } = this.cfg;
|
|
79
|
+
let rawEntries;
|
|
80
|
+
if (!transformer) {
|
|
81
|
+
rawEntries = entries;
|
|
108
82
|
}
|
|
109
83
|
else {
|
|
110
|
-
|
|
84
|
+
rawEntries = await (0, js_lib_1.pMap)(entries, async ([id, v]) => [id, await transformer.valueToRaw(v)]);
|
|
111
85
|
}
|
|
112
|
-
await this.cfg.db.saveBatch(this.cfg.table,
|
|
86
|
+
await this.cfg.db.saveBatch(this.cfg.table, rawEntries, opt);
|
|
113
87
|
}
|
|
114
|
-
async
|
|
88
|
+
async saveBatchRaw(entries, opt) {
|
|
115
89
|
await this.cfg.db.saveBatch(this.cfg.table, entries, opt);
|
|
116
90
|
}
|
|
117
91
|
async deleteByIds(ids) {
|
|
@@ -124,13 +98,13 @@ class CommonKeyValueDao {
|
|
|
124
98
|
return this.cfg.db.streamIds(this.cfg.table, limit);
|
|
125
99
|
}
|
|
126
100
|
streamValues(limit) {
|
|
127
|
-
const {
|
|
128
|
-
if (!
|
|
101
|
+
const { transformer } = this.cfg;
|
|
102
|
+
if (!transformer) {
|
|
129
103
|
return this.cfg.db.streamValues(this.cfg.table, limit);
|
|
130
104
|
}
|
|
131
|
-
return this.cfg.db.streamValues(this.cfg.table, limit).flatMap(async (
|
|
105
|
+
return this.cfg.db.streamValues(this.cfg.table, limit).flatMap(async (raw) => {
|
|
132
106
|
try {
|
|
133
|
-
return [await
|
|
107
|
+
return [await transformer.rawToValue(raw)];
|
|
134
108
|
}
|
|
135
109
|
catch (err) {
|
|
136
110
|
this.cfg.logger.error(err);
|
|
@@ -141,13 +115,13 @@ class CommonKeyValueDao {
|
|
|
141
115
|
});
|
|
142
116
|
}
|
|
143
117
|
streamEntries(limit) {
|
|
144
|
-
const {
|
|
145
|
-
if (!
|
|
118
|
+
const { transformer } = this.cfg;
|
|
119
|
+
if (!transformer) {
|
|
146
120
|
return this.cfg.db.streamEntries(this.cfg.table, limit);
|
|
147
121
|
}
|
|
148
|
-
return this.cfg.db.streamEntries(this.cfg.table, limit).flatMap(async ([id,
|
|
122
|
+
return this.cfg.db.streamEntries(this.cfg.table, limit).flatMap(async ([id, raw]) => {
|
|
149
123
|
try {
|
|
150
|
-
return [[id, await
|
|
124
|
+
return [[id, await transformer.rawToValue(raw)]];
|
|
151
125
|
}
|
|
152
126
|
catch (err) {
|
|
153
127
|
this.cfg.logger.error(err);
|
|
@@ -173,7 +147,11 @@ class CommonKeyValueDao {
|
|
|
173
147
|
* Returns the new value of the field.
|
|
174
148
|
*/
|
|
175
149
|
async increment(id, by = 1) {
|
|
176
|
-
|
|
150
|
+
const [t] = await this.cfg.db.incrementBatch(this.cfg.table, [[id, by]]);
|
|
151
|
+
return t[1];
|
|
152
|
+
}
|
|
153
|
+
async incrementBatch(entries) {
|
|
154
|
+
return await this.cfg.db.incrementBatch(this.cfg.table, entries);
|
|
177
155
|
}
|
|
178
156
|
}
|
|
179
157
|
exports.CommonKeyValueDao = CommonKeyValueDao;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AsyncMemoCache, MISS, NumberOfSeconds } from '@naturalcycles/js-lib';
|
|
2
2
|
import { CommonKeyValueDao } from './commonKeyValueDao';
|
|
3
3
|
export interface CommonKeyValueDaoMemoCacheCfg<VALUE> {
|
|
4
|
-
dao: CommonKeyValueDao<VALUE>;
|
|
4
|
+
dao: CommonKeyValueDao<string, VALUE>;
|
|
5
5
|
/**
|
|
6
6
|
* If set, every `set()` will set `expireAt` (TTL) option.
|
|
7
7
|
*/
|
|
@@ -15,7 +15,7 @@ export interface CommonKeyValueDaoMemoCacheCfg<VALUE> {
|
|
|
15
15
|
* Also, does not support .clear(), as it's more dangerous than useful to actually
|
|
16
16
|
* clear the whole table/cache.
|
|
17
17
|
*/
|
|
18
|
-
export declare class CommonKeyValueDaoMemoCache<VALUE
|
|
18
|
+
export declare class CommonKeyValueDaoMemoCache<VALUE> implements AsyncMemoCache<string, VALUE> {
|
|
19
19
|
private cfg;
|
|
20
20
|
constructor(cfg: CommonKeyValueDaoMemoCacheCfg<VALUE>);
|
|
21
21
|
get(k: string): Promise<VALUE | typeof MISS>;
|
package/dist/query/dbQuery.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AsyncMapper, BaseDBEntity, ObjectWithId } from '@naturalcycles/js-lib';
|
|
2
2
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { CommonDaoOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions
|
|
3
|
+
import { CommonDaoOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions } from '..';
|
|
4
4
|
import { CommonDao } from '../commondao/common.dao';
|
|
5
5
|
import { RunQueryResult } from '../db.model';
|
|
6
6
|
/**
|
|
@@ -99,7 +99,7 @@ export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDB
|
|
|
99
99
|
runQueryExtended(opt?: CommonDaoOptions): Promise<RunQueryResult<BM>>;
|
|
100
100
|
runQueryExtendedAsDBM(opt?: CommonDaoOptions): Promise<RunQueryResult<DBM>>;
|
|
101
101
|
runQueryCount(opt?: CommonDaoOptions): Promise<number>;
|
|
102
|
-
|
|
102
|
+
patchByQuery(patch: Partial<DBM>, opt?: CommonDaoOptions): Promise<number>;
|
|
103
103
|
streamQueryForEach(mapper: AsyncMapper<BM, void>, opt?: CommonDaoStreamForEachOptions<BM>): Promise<void>;
|
|
104
104
|
streamQueryAsDBMForEach(mapper: AsyncMapper<DBM, void>, opt?: CommonDaoStreamForEachOptions<DBM>): Promise<void>;
|
|
105
105
|
streamQuery(opt?: CommonDaoStreamOptions<BM>): ReadableTyped<BM>;
|
package/dist/query/dbQuery.js
CHANGED
|
@@ -165,8 +165,8 @@ class RunnableDBQuery extends DBQuery {
|
|
|
165
165
|
async runQueryCount(opt) {
|
|
166
166
|
return await this.dao.runQueryCount(this, opt);
|
|
167
167
|
}
|
|
168
|
-
async
|
|
169
|
-
return await this.dao.
|
|
168
|
+
async patchByQuery(patch, opt) {
|
|
169
|
+
return await this.dao.patchByQuery(this, patch, opt);
|
|
170
170
|
}
|
|
171
171
|
async streamQueryForEach(mapper, opt) {
|
|
172
172
|
await this.dao.streamQueryForEach(this, mapper, opt);
|
package/dist/testing/daoTest.js
CHANGED
|
@@ -99,6 +99,32 @@ function runCommonDaoTest(db, quirks = {}) {
|
|
|
99
99
|
expect(items[0]).toMatchObject(clone);
|
|
100
100
|
(0, dbTest_1.expectMatch)(expectedItems, itemsSaved, quirks);
|
|
101
101
|
});
|
|
102
|
+
if (support.increment) {
|
|
103
|
+
test('increment', async () => {
|
|
104
|
+
await dao.incrementBatch('k3', { id1: 1, id2: 2 });
|
|
105
|
+
let rows = await dao.query().runQuery();
|
|
106
|
+
rows = (0, js_lib_1._sortBy)(rows, r => r.id);
|
|
107
|
+
const expected = expectedItems.map(r => {
|
|
108
|
+
if (r.id === 'id1') {
|
|
109
|
+
return {
|
|
110
|
+
...r,
|
|
111
|
+
k3: r.k3 + 1,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (r.id === 'id2') {
|
|
115
|
+
return {
|
|
116
|
+
...r,
|
|
117
|
+
k3: r.k3 + 2,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return r;
|
|
121
|
+
});
|
|
122
|
+
(0, dbTest_1.expectMatch)(expected, rows, quirks);
|
|
123
|
+
// reset the changes
|
|
124
|
+
await dao.increment('k3', 'id1', -1);
|
|
125
|
+
await dao.increment('k3', 'id2', -2);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
102
128
|
// GET not empty
|
|
103
129
|
test('getByIds all items', async () => {
|
|
104
130
|
const rows = await dao.getByIds(items.map(i => i.id).concat('abcd'));
|
package/dist/testing/dbTest.js
CHANGED
|
@@ -4,7 +4,6 @@ exports.runCommonDBTest = runCommonDBTest;
|
|
|
4
4
|
exports.expectMatch = expectMatch;
|
|
5
5
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
6
6
|
const common_db_1 = require("../common.db");
|
|
7
|
-
const db_model_1 = require("../db.model");
|
|
8
7
|
const dbQuery_1 = require("../query/dbQuery");
|
|
9
8
|
const test_model_1 = require("./test.model");
|
|
10
9
|
function runCommonDBTest(db, quirks = {}) {
|
|
@@ -236,8 +235,8 @@ function runCommonDBTest(db, quirks = {}) {
|
|
|
236
235
|
expectMatch(expected, rows, quirks);
|
|
237
236
|
});
|
|
238
237
|
}
|
|
239
|
-
if (support.
|
|
240
|
-
test('
|
|
238
|
+
if (support.patchByQuery) {
|
|
239
|
+
test('patchByQuery', async () => {
|
|
241
240
|
// cleanup, reset initial data
|
|
242
241
|
await db.deleteByQuery(queryAll());
|
|
243
242
|
await db.saveBatch(test_model_1.TEST_TABLE, items);
|
|
@@ -245,7 +244,7 @@ function runCommonDBTest(db, quirks = {}) {
|
|
|
245
244
|
k3: 5,
|
|
246
245
|
k2: 'abc',
|
|
247
246
|
};
|
|
248
|
-
await db.
|
|
247
|
+
await db.patchByQuery(dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE).filterEq('even', true), patch);
|
|
249
248
|
const { rows } = await db.runQuery(queryAll());
|
|
250
249
|
const expected = items.map(r => {
|
|
251
250
|
if (r.even) {
|
|
@@ -255,26 +254,25 @@ function runCommonDBTest(db, quirks = {}) {
|
|
|
255
254
|
});
|
|
256
255
|
expectMatch(expected, rows, quirks);
|
|
257
256
|
});
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
});
|
|
275
|
-
expectMatch(expected, rows, quirks);
|
|
257
|
+
}
|
|
258
|
+
if (support.increment) {
|
|
259
|
+
test('incrementBatch', async () => {
|
|
260
|
+
// cleanup, reset initial data
|
|
261
|
+
await db.deleteByQuery(queryAll());
|
|
262
|
+
await db.saveBatch(test_model_1.TEST_TABLE, items);
|
|
263
|
+
await db.incrementBatch(test_model_1.TEST_TABLE, 'k3', { id1: 1, id2: 2 });
|
|
264
|
+
const { rows } = await db.runQuery(queryAll());
|
|
265
|
+
const expected = items.map(r => {
|
|
266
|
+
if (r.id === 'id1') {
|
|
267
|
+
return { ...r, k3: 2 };
|
|
268
|
+
}
|
|
269
|
+
if (r.id === 'id2') {
|
|
270
|
+
return { ...r, k3: 4 };
|
|
271
|
+
}
|
|
272
|
+
return r;
|
|
276
273
|
});
|
|
277
|
-
|
|
274
|
+
expectMatch(expected, rows, quirks);
|
|
275
|
+
});
|
|
278
276
|
}
|
|
279
277
|
if (support.queries) {
|
|
280
278
|
test('cleanup', async () => {
|
|
@@ -4,7 +4,10 @@ exports.runCommonKeyValueDBTest = runCommonKeyValueDBTest;
|
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const test_model_1 = require("./test.model");
|
|
6
6
|
const testIds = (0, js_lib_1._range)(1, 4).map(n => `id${n}`);
|
|
7
|
-
const testEntries = testIds.map(id => [
|
|
7
|
+
const testEntries = testIds.map(id => [
|
|
8
|
+
id,
|
|
9
|
+
Buffer.from(`${id}value`),
|
|
10
|
+
]);
|
|
8
11
|
function runCommonKeyValueDBTest(db) {
|
|
9
12
|
beforeAll(async () => {
|
|
10
13
|
// Tests in this suite are not isolated,
|
|
@@ -86,17 +89,29 @@ function runCommonKeyValueDBTest(db) {
|
|
|
86
89
|
expect(results).toEqual([]);
|
|
87
90
|
});
|
|
88
91
|
if (support.increment) {
|
|
92
|
+
const id = 'nonExistingField';
|
|
93
|
+
const id2 = 'nonExistingField2';
|
|
89
94
|
test('increment on a non-existing field should set the value to 1', async () => {
|
|
90
|
-
const result = await db.
|
|
91
|
-
expect(result).
|
|
95
|
+
const result = await db.incrementBatch(test_model_1.TEST_TABLE, [[id, 1]]);
|
|
96
|
+
expect(result).toEqual([[id, 1]]);
|
|
92
97
|
});
|
|
93
98
|
test('increment on a existing field should increase the value by one', async () => {
|
|
94
|
-
const result = await db.
|
|
95
|
-
expect(result).
|
|
99
|
+
const result = await db.incrementBatch(test_model_1.TEST_TABLE, [[id, 1]]);
|
|
100
|
+
expect(result).toEqual([[id, 2]]);
|
|
96
101
|
});
|
|
97
102
|
test('increment should increase the value by the specified amount', async () => {
|
|
98
|
-
const result = await db.
|
|
99
|
-
expect(result).
|
|
103
|
+
const result = await db.incrementBatch(test_model_1.TEST_TABLE, [[id, 2]]);
|
|
104
|
+
expect(result).toEqual([[id, 4]]);
|
|
105
|
+
});
|
|
106
|
+
test('increment 2 ids at the same time', async () => {
|
|
107
|
+
const result = await db.incrementBatch(test_model_1.TEST_TABLE, [
|
|
108
|
+
[id, 1],
|
|
109
|
+
[id2, 2],
|
|
110
|
+
]);
|
|
111
|
+
expect(Object.fromEntries(result)).toEqual({
|
|
112
|
+
[id]: 5,
|
|
113
|
+
[id2]: 2,
|
|
114
|
+
});
|
|
100
115
|
});
|
|
101
116
|
}
|
|
102
117
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function runCommonKeyValueDaoTest(
|
|
1
|
+
import { CommonKeyValueDB } from '../kv/commonKeyValueDB';
|
|
2
|
+
export declare function runCommonKeyValueDaoTest(db: CommonKeyValueDB): void;
|
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runCommonKeyValueDaoTest = runCommonKeyValueDaoTest;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
5
|
+
const commonKeyValueDao_1 = require("../kv/commonKeyValueDao");
|
|
6
|
+
const test_model_1 = require("./test.model");
|
|
7
|
+
const testItems = (0, test_model_1.createTestItemsBM)(4);
|
|
8
|
+
const testIds = testItems.map(e => e.id);
|
|
9
|
+
const testEntries = testItems.map(e => [e.id, e]);
|
|
10
|
+
function runCommonKeyValueDaoTest(db) {
|
|
11
|
+
const dao = new commonKeyValueDao_1.CommonKeyValueDao({
|
|
12
|
+
db,
|
|
13
|
+
table: test_model_1.TEST_TABLE,
|
|
14
|
+
// todo: make this test support "deflatedJson" transformer
|
|
15
|
+
});
|
|
16
|
+
const { support } = db;
|
|
8
17
|
beforeAll(async () => {
|
|
9
18
|
// Tests in this suite are not isolated,
|
|
10
19
|
// and failing tests can leave the DB in an unexpected state for other tests,
|
|
@@ -17,7 +26,6 @@ function runCommonKeyValueDaoTest(dao) {
|
|
|
17
26
|
const ids = await dao.streamIds().toArray();
|
|
18
27
|
await dao.deleteByIds(ids);
|
|
19
28
|
});
|
|
20
|
-
const { support } = dao.cfg.db;
|
|
21
29
|
test('ping', async () => {
|
|
22
30
|
await dao.ping();
|
|
23
31
|
});
|
|
@@ -34,8 +42,11 @@ function runCommonKeyValueDaoTest(dao) {
|
|
|
34
42
|
test('saveBatch, then getByIds', async () => {
|
|
35
43
|
await dao.saveBatch(testEntries);
|
|
36
44
|
const entries = await dao.getByIds(testIds);
|
|
45
|
+
// console.log(typeof entries[0]![1], entries[0]![1])
|
|
37
46
|
(0, js_lib_1._sortBy)(entries, e => e[0], true);
|
|
38
|
-
expect(entries).toEqual(testEntries);
|
|
47
|
+
expect(entries).toEqual(testEntries); // Jest doesn't allow to compare Buffers directly
|
|
48
|
+
// expect(entries.map(e => e[0])).toEqual(testEntries.map(e => e[0]))
|
|
49
|
+
// expect(entries.map(e => e[1].toString())).toEqual(testEntries.map(e => e[1].toString()))
|
|
39
50
|
});
|
|
40
51
|
test('streamIds', async () => {
|
|
41
52
|
const ids = await dao.streamIds().toArray();
|
|
@@ -77,17 +88,29 @@ function runCommonKeyValueDaoTest(dao) {
|
|
|
77
88
|
expect(results).toEqual([]);
|
|
78
89
|
});
|
|
79
90
|
if (support.increment) {
|
|
91
|
+
const id = 'nonExistingField';
|
|
92
|
+
const id2 = 'nonExistingField2';
|
|
80
93
|
test('increment on a non-existing field should set the value to 1', async () => {
|
|
81
|
-
const result = await dao.increment(
|
|
94
|
+
const result = await dao.increment(id);
|
|
82
95
|
expect(result).toBe(1);
|
|
83
96
|
});
|
|
84
97
|
test('increment on a existing field should increase the value by one', async () => {
|
|
85
|
-
const result = await dao.increment(
|
|
98
|
+
const result = await dao.increment(id);
|
|
86
99
|
expect(result).toBe(2);
|
|
87
100
|
});
|
|
88
101
|
test('increment should increase the value by the specified amount', async () => {
|
|
89
|
-
const result = await dao.increment(
|
|
102
|
+
const result = await dao.increment(id, 2);
|
|
90
103
|
expect(result).toBe(4);
|
|
91
104
|
});
|
|
105
|
+
test('increment 2 ids at the same time', async () => {
|
|
106
|
+
const result = await dao.incrementBatch([
|
|
107
|
+
[id, 1],
|
|
108
|
+
[id2, 2],
|
|
109
|
+
]);
|
|
110
|
+
expect(Object.fromEntries(result)).toEqual({
|
|
111
|
+
[id]: 5,
|
|
112
|
+
[id2]: 2,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
92
115
|
}
|
|
93
116
|
}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
10
10
|
import { BaseCommonDB } from '../../base.common.db'
|
|
11
11
|
import { CommonDB, commonDBFullSupport, CommonDBSupport } from '../../common.db'
|
|
12
|
-
import {
|
|
12
|
+
import { RunQueryResult } from '../../db.model'
|
|
13
13
|
import { DBQuery } from '../../query/dbQuery'
|
|
14
14
|
import {
|
|
15
15
|
CacheDBCfg,
|
|
@@ -29,6 +29,7 @@ export class CacheDB extends BaseCommonDB implements CommonDB {
|
|
|
29
29
|
override support: CommonDBSupport = {
|
|
30
30
|
...commonDBFullSupport,
|
|
31
31
|
transactions: false,
|
|
32
|
+
increment: false,
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
constructor(cfg: CacheDBCfg) {
|
|
@@ -271,19 +272,19 @@ export class CacheDB extends BaseCommonDB implements CommonDB {
|
|
|
271
272
|
return deletedIds
|
|
272
273
|
}
|
|
273
274
|
|
|
274
|
-
override async
|
|
275
|
+
override async patchByQuery<ROW extends ObjectWithId>(
|
|
275
276
|
q: DBQuery<ROW>,
|
|
276
|
-
patch:
|
|
277
|
+
patch: Partial<ROW>,
|
|
277
278
|
opt: CacheDBOptions = {},
|
|
278
279
|
): Promise<number> {
|
|
279
280
|
let updated: number | undefined
|
|
280
281
|
|
|
281
282
|
if (!opt.onlyCache && !this.cfg.onlyCache) {
|
|
282
|
-
updated = await this.cfg.downstreamDB.
|
|
283
|
+
updated = await this.cfg.downstreamDB.patchByQuery(q, patch, opt)
|
|
283
284
|
}
|
|
284
285
|
|
|
285
286
|
if (!opt.skipCache && !this.cfg.skipCache) {
|
|
286
|
-
const cacheResult = this.cfg.cacheDB.
|
|
287
|
+
const cacheResult = this.cfg.cacheDB.patchByQuery(q, patch, opt)
|
|
287
288
|
if (this.cfg.awaitCache) updated ??= await cacheResult
|
|
288
289
|
}
|
|
289
290
|
|
|
@@ -45,9 +45,10 @@ export class FileDB extends BaseCommonDB implements CommonDB {
|
|
|
45
45
|
bufferValues: false, // todo: implement
|
|
46
46
|
insertSaveMethod: false,
|
|
47
47
|
updateSaveMethod: false,
|
|
48
|
-
|
|
48
|
+
patchByQuery: false,
|
|
49
49
|
createTable: false,
|
|
50
50
|
transactions: false, // todo
|
|
51
|
+
increment: false,
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
constructor(cfg: FileDBCfg) {
|
|
@@ -3,9 +3,12 @@ import {
|
|
|
3
3
|
_assert,
|
|
4
4
|
_by,
|
|
5
5
|
_deepCopy,
|
|
6
|
+
_isEmptyObject,
|
|
6
7
|
_since,
|
|
7
8
|
_sortObjectDeep,
|
|
9
|
+
_stringMapEntries,
|
|
8
10
|
_stringMapValues,
|
|
11
|
+
AnyObjectWithId,
|
|
9
12
|
CommonLogger,
|
|
10
13
|
generateJsonSchemaFromData,
|
|
11
14
|
JsonSchemaObject,
|
|
@@ -27,9 +30,7 @@ import {
|
|
|
27
30
|
commonDBFullSupport,
|
|
28
31
|
CommonDBTransactionOptions,
|
|
29
32
|
CommonDBType,
|
|
30
|
-
DBIncrement,
|
|
31
33
|
DBOperation,
|
|
32
|
-
DBPatch,
|
|
33
34
|
DBTransactionFn,
|
|
34
35
|
queryInMemory,
|
|
35
36
|
} from '../..'
|
|
@@ -113,7 +114,7 @@ export class InMemoryDB implements CommonDB {
|
|
|
113
114
|
cfg: InMemoryDBCfg
|
|
114
115
|
|
|
115
116
|
// data[table][id] > {id: 'a', created: ... }
|
|
116
|
-
data: StringMap<StringMap<
|
|
117
|
+
data: StringMap<StringMap<AnyObjectWithId>> = {}
|
|
117
118
|
|
|
118
119
|
/**
|
|
119
120
|
* Returns internal "Data snapshot".
|
|
@@ -233,25 +234,14 @@ export class InMemoryDB implements CommonDB {
|
|
|
233
234
|
return count
|
|
234
235
|
}
|
|
235
236
|
|
|
236
|
-
async
|
|
237
|
+
async patchByQuery<ROW extends ObjectWithId>(
|
|
237
238
|
q: DBQuery<ROW>,
|
|
238
|
-
patch:
|
|
239
|
+
patch: Partial<ROW>,
|
|
239
240
|
): Promise<number> {
|
|
240
|
-
|
|
241
|
-
if (!patchEntries.length) return 0
|
|
242
|
-
|
|
241
|
+
if (_isEmptyObject(patch)) return 0
|
|
243
242
|
const table = this.cfg.tablesPrefix + q.table
|
|
244
243
|
const rows = queryInMemory(q, Object.values(this.data[table] || {}) as ROW[])
|
|
245
|
-
rows.forEach((row
|
|
246
|
-
patchEntries.forEach(([k, v]) => {
|
|
247
|
-
if (v instanceof DBIncrement) {
|
|
248
|
-
row[k] = (row[k] || 0) + v.amount
|
|
249
|
-
} else {
|
|
250
|
-
row[k] = v
|
|
251
|
-
}
|
|
252
|
-
})
|
|
253
|
-
})
|
|
254
|
-
|
|
244
|
+
rows.forEach(row => Object.assign(row, patch))
|
|
255
245
|
return rows.length
|
|
256
246
|
}
|
|
257
247
|
|
|
@@ -294,6 +284,27 @@ export class InMemoryDB implements CommonDB {
|
|
|
294
284
|
}
|
|
295
285
|
}
|
|
296
286
|
|
|
287
|
+
async incrementBatch(
|
|
288
|
+
table: string,
|
|
289
|
+
prop: string,
|
|
290
|
+
incrementMap: StringMap<number>,
|
|
291
|
+
_opt?: CommonDBOptions,
|
|
292
|
+
): Promise<StringMap<number>> {
|
|
293
|
+
const tbl = this.cfg.tablesPrefix + table
|
|
294
|
+
this.data[tbl] ||= {}
|
|
295
|
+
|
|
296
|
+
const result: StringMap<number> = {}
|
|
297
|
+
|
|
298
|
+
for (const [id, by] of _stringMapEntries(incrementMap)) {
|
|
299
|
+
this.data[tbl][id] ||= { id }
|
|
300
|
+
const newValue = ((this.data[tbl][id][prop] as number) || 0) + by
|
|
301
|
+
this.data[tbl][id][prop] = newValue
|
|
302
|
+
result[id] = newValue
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return result
|
|
306
|
+
}
|
|
307
|
+
|
|
297
308
|
/**
|
|
298
309
|
* Flushes all tables (all namespaces) at once.
|
|
299
310
|
*/
|