@naturalcycles/db-lib 9.9.1 → 9.10.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.js +26 -7
- package/dist/kv/commonKeyValueDao.d.ts +9 -2
- package/dist/kv/commonKeyValueDao.js +33 -17
- package/dist/kv/commonKeyValueDaoMemoCache.d.ts +3 -3
- package/dist/kv/commonKeyValueDaoMemoCache.js +2 -5
- package/dist/testing/daoTest.js +1 -3
- package/package.json +1 -1
- package/readme.md +9 -1
- package/src/commondao/common.dao.ts +29 -8
- package/src/kv/commonKeyValueDao.ts +49 -30
- package/src/kv/commonKeyValueDaoMemoCache.ts +4 -9
- package/src/testing/daoTest.ts +2 -6
|
@@ -361,6 +361,17 @@ class CommonDao {
|
|
|
361
361
|
const partialQuery = !!q._selectedFieldNames;
|
|
362
362
|
if (partialQuery)
|
|
363
363
|
return stream;
|
|
364
|
+
// This almost works, but hard to implement `errorMode: THROW_AGGREGATED` in this case
|
|
365
|
+
// return stream.flatMap(async (dbm: DBM) => {
|
|
366
|
+
// if (this.cfg.hooks!.afterLoad) {
|
|
367
|
+
// dbm = (await this.cfg.hooks!.afterLoad(dbm))!
|
|
368
|
+
// if (dbm === null) return [] // SKIP
|
|
369
|
+
// }
|
|
370
|
+
//
|
|
371
|
+
// return [await this.dbmToBM(dbm, opt)] satisfies BM[]
|
|
372
|
+
// }, {
|
|
373
|
+
// concurrency: 16,
|
|
374
|
+
// })
|
|
364
375
|
return (stream
|
|
365
376
|
// optimization: 1 validation is enough
|
|
366
377
|
// .pipe(transformMap<any, DBM>(dbm => this.anyToDBM(dbm, opt), safeOpt))
|
|
@@ -376,10 +387,11 @@ class CommonDao {
|
|
|
376
387
|
}, {
|
|
377
388
|
errorMode: opt.errorMode,
|
|
378
389
|
}))
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
390
|
+
// this can make the stream async-iteration-friendly
|
|
391
|
+
// but not applying it now for perf reasons
|
|
392
|
+
// UPD: applying, to be compliant with `.toArray()`, etc.
|
|
393
|
+
.on('error', err => stream.emit('error', err))
|
|
394
|
+
.pipe((0, nodejs_lib_1.transformNoOp)()));
|
|
383
395
|
}
|
|
384
396
|
async queryIds(q, opt = {}) {
|
|
385
397
|
q.table = opt.table || q.table;
|
|
@@ -389,12 +401,19 @@ class CommonDao {
|
|
|
389
401
|
streamQueryIds(q, opt = {}) {
|
|
390
402
|
q.table = opt.table || q.table;
|
|
391
403
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
404
|
+
// Experimental: using `.map()`
|
|
392
405
|
const stream = this.cfg.db
|
|
393
406
|
.streamQuery(q.select(['id']), opt)
|
|
394
407
|
.on('error', err => stream.emit('error', err))
|
|
395
|
-
.
|
|
396
|
-
|
|
397
|
-
|
|
408
|
+
.map((r) => r.id);
|
|
409
|
+
// const stream: ReadableTyped<string> = this.cfg.db
|
|
410
|
+
// .streamQuery<DBM>(q.select(['id']), opt)
|
|
411
|
+
// .on('error', err => stream.emit('error', err))
|
|
412
|
+
// .pipe(
|
|
413
|
+
// transformMapSimple<DBM, string>(r => r.id, {
|
|
414
|
+
// errorMode: ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
415
|
+
// }),
|
|
416
|
+
// )
|
|
398
417
|
return stream;
|
|
399
418
|
}
|
|
400
419
|
async streamQueryIdsForEach(q, mapper, opt = {}) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { KeyValueTuple } from '@naturalcycles/js-lib';
|
|
2
|
+
import { CommonLogger, KeyValueTuple } from '@naturalcycles/js-lib';
|
|
3
3
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
4
4
|
import { CommonDaoLogLevel } from '../commondao/common.dao.model';
|
|
5
5
|
import { CommonDBCreateOptions } from '../db.model';
|
|
@@ -12,6 +12,10 @@ export interface CommonKeyValueDaoCfg<T> {
|
|
|
12
12
|
* Set to true to limit DB writing (will throw an error is such case).
|
|
13
13
|
*/
|
|
14
14
|
readOnly?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Default to console
|
|
17
|
+
*/
|
|
18
|
+
logger?: CommonLogger;
|
|
15
19
|
/**
|
|
16
20
|
* @default OPERATIONS
|
|
17
21
|
*/
|
|
@@ -33,8 +37,11 @@ export interface CommonKeyValueDaoCfg<T> {
|
|
|
33
37
|
deflatedJsonValue?: boolean;
|
|
34
38
|
}
|
|
35
39
|
export declare class CommonKeyValueDao<T> {
|
|
36
|
-
cfg: CommonKeyValueDaoCfg<T>;
|
|
37
40
|
constructor(cfg: CommonKeyValueDaoCfg<T>);
|
|
41
|
+
cfg: CommonKeyValueDaoCfg<T> & {
|
|
42
|
+
hooks: NonNullable<CommonKeyValueDaoCfg<T>['hooks']>;
|
|
43
|
+
logger: CommonLogger;
|
|
44
|
+
};
|
|
38
45
|
ping(): Promise<void>;
|
|
39
46
|
createTable(opt?: CommonDBCreateOptions): Promise<void>;
|
|
40
47
|
create(input?: Partial<T>): T;
|
|
@@ -7,9 +7,13 @@ const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
|
7
7
|
// todo: readonly
|
|
8
8
|
class CommonKeyValueDao {
|
|
9
9
|
constructor(cfg) {
|
|
10
|
-
this.cfg =
|
|
10
|
+
this.cfg = {
|
|
11
|
+
hooks: {},
|
|
12
|
+
logger: console,
|
|
13
|
+
...cfg,
|
|
14
|
+
};
|
|
11
15
|
if (cfg.deflatedJsonValue) {
|
|
12
|
-
cfg.hooks = {
|
|
16
|
+
this.cfg.hooks = {
|
|
13
17
|
mapValueToBuffer: async (v) => await (0, nodejs_lib_1.deflateString)(JSON.stringify(v)),
|
|
14
18
|
mapBufferToValue: async (buf) => JSON.parse(await (0, nodejs_lib_1.inflateToString)(buf)),
|
|
15
19
|
...cfg.hooks,
|
|
@@ -24,7 +28,7 @@ class CommonKeyValueDao {
|
|
|
24
28
|
}
|
|
25
29
|
create(input = {}) {
|
|
26
30
|
return {
|
|
27
|
-
...this.cfg.hooks
|
|
31
|
+
...this.cfg.hooks.beforeCreate?.(input),
|
|
28
32
|
};
|
|
29
33
|
}
|
|
30
34
|
async getById(id) {
|
|
@@ -66,7 +70,7 @@ class CommonKeyValueDao {
|
|
|
66
70
|
if (r)
|
|
67
71
|
return r[1];
|
|
68
72
|
return {
|
|
69
|
-
...this.cfg.hooks
|
|
73
|
+
...this.cfg.hooks.beforeCreate?.({}),
|
|
70
74
|
...part,
|
|
71
75
|
};
|
|
72
76
|
}
|
|
@@ -80,7 +84,7 @@ class CommonKeyValueDao {
|
|
|
80
84
|
}
|
|
81
85
|
async getByIds(ids) {
|
|
82
86
|
const entries = await this.cfg.db.getByIds(this.cfg.table, ids);
|
|
83
|
-
if (!this.cfg.hooks
|
|
87
|
+
if (!this.cfg.hooks.mapBufferToValue)
|
|
84
88
|
return entries;
|
|
85
89
|
return await (0, js_lib_1.pMap)(entries, async ([id, buf]) => [
|
|
86
90
|
id,
|
|
@@ -97,8 +101,8 @@ class CommonKeyValueDao {
|
|
|
97
101
|
await this.cfg.db.saveBatch(this.cfg.table, [[id, value]]);
|
|
98
102
|
}
|
|
99
103
|
async saveBatch(entries) {
|
|
100
|
-
let bufferEntries
|
|
101
|
-
if (!this.cfg.hooks
|
|
104
|
+
let bufferEntries;
|
|
105
|
+
if (!this.cfg.hooks.mapValueToBuffer) {
|
|
102
106
|
bufferEntries = entries;
|
|
103
107
|
}
|
|
104
108
|
else {
|
|
@@ -122,29 +126,41 @@ class CommonKeyValueDao {
|
|
|
122
126
|
return this.cfg.db.streamIds(this.cfg.table, limit);
|
|
123
127
|
}
|
|
124
128
|
streamValues(limit) {
|
|
125
|
-
|
|
129
|
+
const { mapBufferToValue } = this.cfg.hooks;
|
|
130
|
+
if (!mapBufferToValue) {
|
|
126
131
|
return this.cfg.db.streamValues(this.cfg.table, limit);
|
|
127
132
|
}
|
|
128
|
-
// todo: consider it when readableMap supports `errorMode: SUPPRESS`
|
|
129
|
-
// readableMap(this.cfg.db.streamValues(this.cfg.table, limit), async buf => await this.cfg.hooks!.mapBufferToValue(buf))
|
|
130
133
|
const stream = this.cfg.db
|
|
131
134
|
.streamValues(this.cfg.table, limit)
|
|
132
135
|
.on('error', err => stream.emit('error', err))
|
|
133
|
-
.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
.flatMap(async (buf) => {
|
|
137
|
+
try {
|
|
138
|
+
return [await mapBufferToValue(buf)];
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
this.cfg.logger.error(err);
|
|
142
|
+
return []; // SKIP
|
|
143
|
+
}
|
|
144
|
+
});
|
|
136
145
|
return stream;
|
|
137
146
|
}
|
|
138
147
|
streamEntries(limit) {
|
|
139
|
-
|
|
148
|
+
const { mapBufferToValue } = this.cfg.hooks;
|
|
149
|
+
if (!mapBufferToValue) {
|
|
140
150
|
return this.cfg.db.streamEntries(this.cfg.table, limit);
|
|
141
151
|
}
|
|
142
152
|
const stream = this.cfg.db
|
|
143
153
|
.streamEntries(this.cfg.table, limit)
|
|
144
154
|
.on('error', err => stream.emit('error', err))
|
|
145
|
-
.
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
.flatMap(async ([id, buf]) => {
|
|
156
|
+
try {
|
|
157
|
+
return [[id, await mapBufferToValue(buf)]];
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
this.cfg.logger.error(err);
|
|
161
|
+
return []; // SKIP
|
|
162
|
+
}
|
|
163
|
+
});
|
|
148
164
|
return stream;
|
|
149
165
|
}
|
|
150
166
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsyncMemoCache } from '@naturalcycles/js-lib';
|
|
1
|
+
import { AsyncMemoCache, MISS } from '@naturalcycles/js-lib';
|
|
2
2
|
import { CommonKeyValueDao } from './commonKeyValueDao';
|
|
3
3
|
/**
|
|
4
4
|
* AsyncMemoCache implementation, backed by CommonKeyValueDao.
|
|
@@ -11,7 +11,7 @@ import { CommonKeyValueDao } from './commonKeyValueDao';
|
|
|
11
11
|
export declare class CommonKeyValueDaoMemoCache<VALUE = any> implements AsyncMemoCache<string, VALUE> {
|
|
12
12
|
private dao;
|
|
13
13
|
constructor(dao: CommonKeyValueDao<VALUE>);
|
|
14
|
-
get(k: string): Promise<VALUE |
|
|
15
|
-
set(k: string, v: VALUE
|
|
14
|
+
get(k: string): Promise<VALUE | typeof MISS>;
|
|
15
|
+
set(k: string, v: VALUE): Promise<void>;
|
|
16
16
|
clear(): Promise<void>;
|
|
17
17
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CommonKeyValueDaoMemoCache = void 0;
|
|
4
|
+
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
4
5
|
/**
|
|
5
6
|
* AsyncMemoCache implementation, backed by CommonKeyValueDao.
|
|
6
7
|
*
|
|
@@ -14,13 +15,9 @@ class CommonKeyValueDaoMemoCache {
|
|
|
14
15
|
this.dao = dao;
|
|
15
16
|
}
|
|
16
17
|
async get(k) {
|
|
17
|
-
return (await this.dao.getById(k)) ||
|
|
18
|
+
return (await this.dao.getById(k)) || js_lib_1.MISS;
|
|
18
19
|
}
|
|
19
20
|
async set(k, v) {
|
|
20
|
-
if (v instanceof Error) {
|
|
21
|
-
// We currently don't persist errors there
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
21
|
await this.dao.save(k, v);
|
|
25
22
|
}
|
|
26
23
|
async clear() {
|
package/dist/testing/daoTest.js
CHANGED
|
@@ -144,9 +144,7 @@ function runCommonDaoTest(db, quirks = {}) {
|
|
|
144
144
|
(0, dbTest_1.expectMatch)(expectedItems, rows, quirks);
|
|
145
145
|
});
|
|
146
146
|
test('streamQuery all', async () => {
|
|
147
|
-
|
|
148
|
-
// todo: remove transformNoOp after `transformMap` learns to be async-iteration-friendly
|
|
149
|
-
let rows = await (0, nodejs_lib_1.readableToArray)(dao.query().streamQuery().pipe((0, nodejs_lib_1.transformNoOp)()));
|
|
147
|
+
let rows = await dao.query().streamQuery().toArray();
|
|
150
148
|
rows = (0, js_lib_1._sortBy)(rows, r => r.id);
|
|
151
149
|
(0, dbTest_1.expectMatch)(expectedItems, rows, quirks);
|
|
152
150
|
});
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -150,7 +150,7 @@ Returns `ReadableTyped` (typed wrapper of Node.js
|
|
|
150
150
|
|
|
151
151
|
Streams in Node.js support back-pressure by default (if piped properly by the consumer).
|
|
152
152
|
|
|
153
|
-
```
|
|
153
|
+
```ts
|
|
154
154
|
const q = DBQuery.create('table1') // "return all items" query
|
|
155
155
|
|
|
156
156
|
await _pipeline([
|
|
@@ -165,6 +165,14 @@ await _pipeline([
|
|
|
165
165
|
// ...
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
+
Alternative:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
await db.streamQuery(q).forEach(item => {
|
|
172
|
+
console.log(item)
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
168
176
|
###### saveBatch
|
|
169
177
|
|
|
170
178
|
`saveBatch<DBM>(table: string, dbms: DBM[]): Promise<void>`
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
transformLogProgress,
|
|
41
41
|
transformMap,
|
|
42
42
|
transformMapSimple,
|
|
43
|
+
transformNoOp,
|
|
43
44
|
writableVoid,
|
|
44
45
|
} from '@naturalcycles/nodejs-lib'
|
|
45
46
|
import { DBLibError } from '../cnst'
|
|
@@ -496,6 +497,18 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
496
497
|
const partialQuery = !!q._selectedFieldNames
|
|
497
498
|
if (partialQuery) return stream
|
|
498
499
|
|
|
500
|
+
// This almost works, but hard to implement `errorMode: THROW_AGGREGATED` in this case
|
|
501
|
+
// return stream.flatMap(async (dbm: DBM) => {
|
|
502
|
+
// if (this.cfg.hooks!.afterLoad) {
|
|
503
|
+
// dbm = (await this.cfg.hooks!.afterLoad(dbm))!
|
|
504
|
+
// if (dbm === null) return [] // SKIP
|
|
505
|
+
// }
|
|
506
|
+
//
|
|
507
|
+
// return [await this.dbmToBM(dbm, opt)] satisfies BM[]
|
|
508
|
+
// }, {
|
|
509
|
+
// concurrency: 16,
|
|
510
|
+
// })
|
|
511
|
+
|
|
499
512
|
return (
|
|
500
513
|
stream
|
|
501
514
|
// optimization: 1 validation is enough
|
|
@@ -517,9 +530,11 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
517
530
|
},
|
|
518
531
|
),
|
|
519
532
|
)
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
533
|
+
// this can make the stream async-iteration-friendly
|
|
534
|
+
// but not applying it now for perf reasons
|
|
535
|
+
// UPD: applying, to be compliant with `.toArray()`, etc.
|
|
536
|
+
.on('error', err => stream.emit('error', err))
|
|
537
|
+
.pipe(transformNoOp())
|
|
523
538
|
)
|
|
524
539
|
}
|
|
525
540
|
|
|
@@ -533,14 +548,20 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
533
548
|
q.table = opt.table || q.table
|
|
534
549
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
535
550
|
|
|
551
|
+
// Experimental: using `.map()`
|
|
536
552
|
const stream: ReadableTyped<string> = this.cfg.db
|
|
537
553
|
.streamQuery<DBM>(q.select(['id']), opt)
|
|
538
554
|
.on('error', err => stream.emit('error', err))
|
|
539
|
-
.
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
555
|
+
.map((r: ObjectWithId) => r.id)
|
|
556
|
+
|
|
557
|
+
// const stream: ReadableTyped<string> = this.cfg.db
|
|
558
|
+
// .streamQuery<DBM>(q.select(['id']), opt)
|
|
559
|
+
// .on('error', err => stream.emit('error', err))
|
|
560
|
+
// .pipe(
|
|
561
|
+
// transformMapSimple<DBM, string>(r => r.id, {
|
|
562
|
+
// errorMode: ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
563
|
+
// }),
|
|
564
|
+
// )
|
|
544
565
|
|
|
545
566
|
return stream
|
|
546
567
|
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import { AppError,
|
|
2
|
-
import {
|
|
3
|
-
deflateString,
|
|
4
|
-
inflateToString,
|
|
5
|
-
ReadableTyped,
|
|
6
|
-
transformMap,
|
|
7
|
-
} from '@naturalcycles/nodejs-lib'
|
|
1
|
+
import { AppError, CommonLogger, KeyValueTuple, pMap } from '@naturalcycles/js-lib'
|
|
2
|
+
import { deflateString, inflateToString, ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
8
3
|
import { CommonDaoLogLevel } from '../commondao/common.dao.model'
|
|
9
4
|
import { CommonDBCreateOptions } from '../db.model'
|
|
10
5
|
import { CommonKeyValueDB, KeyValueDBTuple } from './commonKeyValueDB'
|
|
@@ -20,6 +15,11 @@ export interface CommonKeyValueDaoCfg<T> {
|
|
|
20
15
|
*/
|
|
21
16
|
readOnly?: boolean
|
|
22
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Default to console
|
|
20
|
+
*/
|
|
21
|
+
logger?: CommonLogger
|
|
22
|
+
|
|
23
23
|
/**
|
|
24
24
|
* @default OPERATIONS
|
|
25
25
|
*/
|
|
@@ -48,9 +48,15 @@ export interface CommonKeyValueDaoCfg<T> {
|
|
|
48
48
|
// todo: readonly
|
|
49
49
|
|
|
50
50
|
export class CommonKeyValueDao<T> {
|
|
51
|
-
constructor(
|
|
51
|
+
constructor(cfg: CommonKeyValueDaoCfg<T>) {
|
|
52
|
+
this.cfg = {
|
|
53
|
+
hooks: {},
|
|
54
|
+
logger: console,
|
|
55
|
+
...cfg,
|
|
56
|
+
}
|
|
57
|
+
|
|
52
58
|
if (cfg.deflatedJsonValue) {
|
|
53
|
-
cfg.hooks = {
|
|
59
|
+
this.cfg.hooks = {
|
|
54
60
|
mapValueToBuffer: async v => await deflateString(JSON.stringify(v)),
|
|
55
61
|
mapBufferToValue: async buf => JSON.parse(await inflateToString(buf)),
|
|
56
62
|
...cfg.hooks,
|
|
@@ -58,6 +64,11 @@ export class CommonKeyValueDao<T> {
|
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
cfg: CommonKeyValueDaoCfg<T> & {
|
|
68
|
+
hooks: NonNullable<CommonKeyValueDaoCfg<T>['hooks']>
|
|
69
|
+
logger: CommonLogger
|
|
70
|
+
}
|
|
71
|
+
|
|
61
72
|
async ping(): Promise<void> {
|
|
62
73
|
await this.cfg.db.ping()
|
|
63
74
|
}
|
|
@@ -68,7 +79,7 @@ export class CommonKeyValueDao<T> {
|
|
|
68
79
|
|
|
69
80
|
create(input: Partial<T> = {}): T {
|
|
70
81
|
return {
|
|
71
|
-
...this.cfg.hooks
|
|
82
|
+
...this.cfg.hooks.beforeCreate?.(input),
|
|
72
83
|
} as T
|
|
73
84
|
}
|
|
74
85
|
|
|
@@ -117,7 +128,7 @@ export class CommonKeyValueDao<T> {
|
|
|
117
128
|
if (r) return r[1]
|
|
118
129
|
|
|
119
130
|
return {
|
|
120
|
-
...this.cfg.hooks
|
|
131
|
+
...this.cfg.hooks.beforeCreate?.({}),
|
|
121
132
|
...part,
|
|
122
133
|
} as T
|
|
123
134
|
}
|
|
@@ -135,11 +146,11 @@ export class CommonKeyValueDao<T> {
|
|
|
135
146
|
|
|
136
147
|
async getByIds(ids: string[]): Promise<KeyValueTuple<string, T>[]> {
|
|
137
148
|
const entries = await this.cfg.db.getByIds(this.cfg.table, ids)
|
|
138
|
-
if (!this.cfg.hooks
|
|
149
|
+
if (!this.cfg.hooks.mapBufferToValue) return entries as any
|
|
139
150
|
|
|
140
151
|
return await pMap(entries, async ([id, buf]) => [
|
|
141
152
|
id,
|
|
142
|
-
await this.cfg.hooks
|
|
153
|
+
await this.cfg.hooks.mapBufferToValue!(buf),
|
|
143
154
|
])
|
|
144
155
|
}
|
|
145
156
|
|
|
@@ -156,14 +167,14 @@ export class CommonKeyValueDao<T> {
|
|
|
156
167
|
}
|
|
157
168
|
|
|
158
169
|
async saveBatch(entries: KeyValueTuple<string, T>[]): Promise<void> {
|
|
159
|
-
let bufferEntries: KeyValueDBTuple[]
|
|
170
|
+
let bufferEntries: KeyValueDBTuple[]
|
|
160
171
|
|
|
161
|
-
if (!this.cfg.hooks
|
|
172
|
+
if (!this.cfg.hooks.mapValueToBuffer) {
|
|
162
173
|
bufferEntries = entries as any
|
|
163
174
|
} else {
|
|
164
175
|
bufferEntries = await pMap(entries, async ([id, v]) => [
|
|
165
176
|
id,
|
|
166
|
-
await this.cfg.hooks
|
|
177
|
+
await this.cfg.hooks.mapValueToBuffer!(v),
|
|
167
178
|
])
|
|
168
179
|
}
|
|
169
180
|
|
|
@@ -187,37 +198,45 @@ export class CommonKeyValueDao<T> {
|
|
|
187
198
|
}
|
|
188
199
|
|
|
189
200
|
streamValues(limit?: number): ReadableTyped<T> {
|
|
190
|
-
|
|
201
|
+
const { mapBufferToValue } = this.cfg.hooks
|
|
202
|
+
|
|
203
|
+
if (!mapBufferToValue) {
|
|
191
204
|
return this.cfg.db.streamValues(this.cfg.table, limit)
|
|
192
205
|
}
|
|
193
206
|
|
|
194
|
-
// todo: consider it when readableMap supports `errorMode: SUPPRESS`
|
|
195
|
-
// readableMap(this.cfg.db.streamValues(this.cfg.table, limit), async buf => await this.cfg.hooks!.mapBufferToValue(buf))
|
|
196
207
|
const stream: ReadableTyped<T> = this.cfg.db
|
|
197
208
|
.streamValues(this.cfg.table, limit)
|
|
198
209
|
.on('error', err => stream.emit('error', err))
|
|
199
|
-
.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
})
|
|
203
|
-
|
|
210
|
+
.flatMap(async (buf: Buffer) => {
|
|
211
|
+
try {
|
|
212
|
+
return [await mapBufferToValue(buf)] satisfies T[]
|
|
213
|
+
} catch (err) {
|
|
214
|
+
this.cfg.logger.error(err)
|
|
215
|
+
return [] // SKIP
|
|
216
|
+
}
|
|
217
|
+
})
|
|
204
218
|
|
|
205
219
|
return stream
|
|
206
220
|
}
|
|
207
221
|
|
|
208
222
|
streamEntries(limit?: number): ReadableTyped<KeyValueTuple<string, T>> {
|
|
209
|
-
|
|
223
|
+
const { mapBufferToValue } = this.cfg.hooks
|
|
224
|
+
|
|
225
|
+
if (!mapBufferToValue) {
|
|
210
226
|
return this.cfg.db.streamEntries(this.cfg.table, limit)
|
|
211
227
|
}
|
|
212
228
|
|
|
213
229
|
const stream: ReadableTyped<KeyValueTuple<string, T>> = this.cfg.db
|
|
214
230
|
.streamEntries(this.cfg.table, limit)
|
|
215
231
|
.on('error', err => stream.emit('error', err))
|
|
216
|
-
.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
})
|
|
220
|
-
|
|
232
|
+
.flatMap(async ([id, buf]: KeyValueTuple<string, Buffer>) => {
|
|
233
|
+
try {
|
|
234
|
+
return [[id, await mapBufferToValue(buf)]] satisfies KeyValueTuple<string, T>[]
|
|
235
|
+
} catch (err) {
|
|
236
|
+
this.cfg.logger.error(err)
|
|
237
|
+
return [] // SKIP
|
|
238
|
+
}
|
|
239
|
+
})
|
|
221
240
|
|
|
222
241
|
return stream
|
|
223
242
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsyncMemoCache } from '@naturalcycles/js-lib'
|
|
1
|
+
import { AsyncMemoCache, MISS } from '@naturalcycles/js-lib'
|
|
2
2
|
import { CommonKeyValueDao } from './commonKeyValueDao'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -12,16 +12,11 @@ import { CommonKeyValueDao } from './commonKeyValueDao'
|
|
|
12
12
|
export class CommonKeyValueDaoMemoCache<VALUE = any> implements AsyncMemoCache<string, VALUE> {
|
|
13
13
|
constructor(private dao: CommonKeyValueDao<VALUE>) {}
|
|
14
14
|
|
|
15
|
-
async get(k: string): Promise<VALUE |
|
|
16
|
-
return (await this.dao.getById(k)) ||
|
|
15
|
+
async get(k: string): Promise<VALUE | typeof MISS> {
|
|
16
|
+
return (await this.dao.getById(k)) || MISS
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
async set(k: string, v: VALUE
|
|
20
|
-
if (v instanceof Error) {
|
|
21
|
-
// We currently don't persist errors there
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
|
|
19
|
+
async set(k: string, v: VALUE): Promise<void> {
|
|
25
20
|
await this.dao.save(k, v)
|
|
26
21
|
}
|
|
27
22
|
|
package/src/testing/daoTest.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Readable } from 'node:stream'
|
|
2
2
|
import { _deepCopy, _pick, _sortBy, _omit, localTimeNow } from '@naturalcycles/js-lib'
|
|
3
|
-
import { _pipeline, readableToArray
|
|
3
|
+
import { _pipeline, readableToArray } from '@naturalcycles/nodejs-lib'
|
|
4
4
|
import { CommonDaoLogLevel, DBQuery } from '..'
|
|
5
5
|
import { CommonDB } from '../common.db'
|
|
6
6
|
import { CommonDao } from '../commondao/common.dao'
|
|
@@ -194,11 +194,7 @@ export function runCommonDaoTest(db: CommonDB, quirks: CommonDBImplementationQui
|
|
|
194
194
|
})
|
|
195
195
|
|
|
196
196
|
test('streamQuery all', async () => {
|
|
197
|
-
|
|
198
|
-
// todo: remove transformNoOp after `transformMap` learns to be async-iteration-friendly
|
|
199
|
-
let rows: TestItemBM[] = await readableToArray(
|
|
200
|
-
dao.query().streamQuery().pipe(transformNoOp()),
|
|
201
|
-
)
|
|
197
|
+
let rows: TestItemBM[] = await dao.query().streamQuery().toArray()
|
|
202
198
|
|
|
203
199
|
rows = _sortBy(rows, r => r.id)
|
|
204
200
|
expectMatch(expectedItems, rows, quirks)
|