@naturalcycles/datastore-lib 4.10.0 → 4.12.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/datastore.db.d.ts +5 -2
- package/dist/datastore.db.js +62 -6
- package/package.json +1 -1
- package/src/datastore.db.ts +88 -7
package/dist/datastore.db.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { CommonDB, CommonDBOptions, CommonDBReadOptions, CommonDBSaveOption
|
|
|
3
3
|
import { BaseCommonDB } from '@naturalcycles/db-lib';
|
|
4
4
|
import type { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib/json-schema';
|
|
5
5
|
import type { CommonLogger } from '@naturalcycles/js-lib/log';
|
|
6
|
-
import type
|
|
6
|
+
import { type ObjectWithId, type StringMap } from '@naturalcycles/js-lib/types';
|
|
7
7
|
import type { ReadableTyped } from '@naturalcycles/nodejs-lib/stream';
|
|
8
8
|
import type { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBReadOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePropertyStats, DatastoreStats } from './datastore.model.js';
|
|
9
9
|
/**
|
|
@@ -27,6 +27,7 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
27
27
|
private getDatastoreLib;
|
|
28
28
|
ping(): Promise<void>;
|
|
29
29
|
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: DatastoreDBReadOptions): Promise<ROW[]>;
|
|
30
|
+
multiGetByIds<ROW extends ObjectWithId>(map: StringMap<string[]>, opt?: DatastoreDBReadOptions): Promise<StringMap<ROW[]>>;
|
|
30
31
|
runQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<RunQueryResult<ROW>>;
|
|
31
32
|
runQueryCount<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<number>;
|
|
32
33
|
private runDatastoreQuery;
|
|
@@ -41,6 +42,7 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
41
42
|
* regardless if they were actually deleted or not.
|
|
42
43
|
*/
|
|
43
44
|
deleteByIds(table: string, ids: string[], opt?: DatastoreDBOptions): Promise<number>;
|
|
45
|
+
multiDeleteByIds(map: StringMap<string[]>, opt?: DatastoreDBOptions): Promise<number>;
|
|
44
46
|
createTransaction(opt?: CommonDBTransactionOptions): Promise<DatastoreDBTransaction>;
|
|
45
47
|
runInTransaction(fn: DBTransactionFn, opt?: CommonDBTransactionOptions): Promise<void>;
|
|
46
48
|
getAllStats(): Promise<DatastoreStats[]>;
|
|
@@ -51,10 +53,11 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
51
53
|
getStatsCount(table: string): Promise<number | undefined>;
|
|
52
54
|
getTableProperties(table: string): Promise<DatastorePropertyStats[]>;
|
|
53
55
|
private mapId;
|
|
56
|
+
private parseDatastoreEntity;
|
|
54
57
|
private toDatastoreEntity;
|
|
55
58
|
key(ds: Datastore, kind: string, id: string): Key;
|
|
56
59
|
private getDsKey;
|
|
57
|
-
|
|
60
|
+
private getIdFromKey;
|
|
58
61
|
createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>): Promise<void>;
|
|
59
62
|
getTables(): Promise<string[]>;
|
|
60
63
|
getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
|
package/dist/datastore.db.js
CHANGED
|
@@ -9,6 +9,7 @@ import { _omit } from '@naturalcycles/js-lib/object/object.util.js';
|
|
|
9
9
|
import { pMap } from '@naturalcycles/js-lib/promise/pMap.js';
|
|
10
10
|
import { pRetry, pRetryFn } from '@naturalcycles/js-lib/promise/pRetry.js';
|
|
11
11
|
import { pTimeout } from '@naturalcycles/js-lib/promise/pTimeout.js';
|
|
12
|
+
import { _stringMapEntries, _stringMapValues, } from '@naturalcycles/js-lib/types';
|
|
12
13
|
import { boldWhite } from '@naturalcycles/nodejs-lib/colors';
|
|
13
14
|
import { DatastoreType } from './datastore.model.js';
|
|
14
15
|
import { DatastoreStreamReadable } from './DatastoreStreamReadable.js';
|
|
@@ -44,6 +45,7 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
44
45
|
support = {
|
|
45
46
|
...commonDBFullSupport,
|
|
46
47
|
patchByQuery: false,
|
|
48
|
+
patchById: false, // use Firestore for that
|
|
47
49
|
increment: false,
|
|
48
50
|
};
|
|
49
51
|
constructor(cfg = {}) {
|
|
@@ -128,7 +130,7 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
128
130
|
catch (err) {
|
|
129
131
|
if (err instanceof TimeoutError) {
|
|
130
132
|
_errorDataAppend(err, {
|
|
131
|
-
fingerprint:
|
|
133
|
+
fingerprint: DATASTORE_TIMEOUT,
|
|
132
134
|
});
|
|
133
135
|
}
|
|
134
136
|
throw err;
|
|
@@ -144,7 +146,29 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
144
146
|
.map(r => this.mapId(r))
|
|
145
147
|
// Seems like datastore .get() method doesn't return items properly sorted by input ids, so we gonna sort them here
|
|
146
148
|
// same ids are not expected here
|
|
147
|
-
.sort(
|
|
149
|
+
.sort(idComparator));
|
|
150
|
+
}
|
|
151
|
+
async multiGetByIds(map, opt = {}) {
|
|
152
|
+
const result = {};
|
|
153
|
+
const ds = await this.ds();
|
|
154
|
+
const dsOpt = this.getRunQueryOptions(opt);
|
|
155
|
+
const keys = [];
|
|
156
|
+
for (const [table, ids] of _stringMapEntries(map)) {
|
|
157
|
+
result[table] = [];
|
|
158
|
+
keys.push(...ids.map(id => this.key(ds, table, id)));
|
|
159
|
+
}
|
|
160
|
+
const r = await ds.get(keys, dsOpt);
|
|
161
|
+
const rows = r[0];
|
|
162
|
+
rows.forEach(entity => {
|
|
163
|
+
const [kind, row] = this.parseDatastoreEntity(entity);
|
|
164
|
+
result[kind].push(row);
|
|
165
|
+
});
|
|
166
|
+
// Seems like datastore .get() method doesn't return items properly sorted by input ids, so we gonna sort them here
|
|
167
|
+
// same ids are not expected here
|
|
168
|
+
for (const tableRows of _stringMapValues(result)) {
|
|
169
|
+
tableRows.sort(idComparator);
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
148
172
|
}
|
|
149
173
|
// getQueryKind(q: Query): string {
|
|
150
174
|
// if (!q?.kinds?.length) return '' // should never be the case, but
|
|
@@ -232,7 +256,7 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
232
256
|
catch (err) {
|
|
233
257
|
if (err instanceof TimeoutError) {
|
|
234
258
|
_errorDataAppend(err, {
|
|
235
|
-
fingerprint:
|
|
259
|
+
fingerprint: DATASTORE_TIMEOUT,
|
|
236
260
|
});
|
|
237
261
|
}
|
|
238
262
|
// console.log(`datastore.save ${kind}`, { obj, entity })
|
|
@@ -240,6 +264,7 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
240
264
|
throw err;
|
|
241
265
|
}
|
|
242
266
|
}
|
|
267
|
+
// not implementing multiSaveBatch, since the API does not support passing excludeFromIndexes for multiple tables
|
|
243
268
|
async deleteByQuery(q, opt = {}) {
|
|
244
269
|
const idFilter = q._filters.find(f => f.name === 'id');
|
|
245
270
|
if (idFilter) {
|
|
@@ -271,6 +296,24 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
271
296
|
});
|
|
272
297
|
return ids.length;
|
|
273
298
|
}
|
|
299
|
+
async multiDeleteByIds(map, opt = {}) {
|
|
300
|
+
const ds = await this.ds();
|
|
301
|
+
const keys = [];
|
|
302
|
+
for (const [table, ids] of _stringMapEntries(map)) {
|
|
303
|
+
keys.push(...ids.map(id => this.key(ds, table, id)));
|
|
304
|
+
}
|
|
305
|
+
const retryOptions = this.getPRetryOptions(`DatastoreLib.multiDeleteByIds`);
|
|
306
|
+
await pMap(_chunk(keys, MAX_ITEMS),
|
|
307
|
+
// async batch => await doDelete(batch),
|
|
308
|
+
async (batchOfKeys) => {
|
|
309
|
+
await pRetry(async () => {
|
|
310
|
+
await (opt.tx?.tx || ds).delete(batchOfKeys);
|
|
311
|
+
}, retryOptions);
|
|
312
|
+
}, {
|
|
313
|
+
concurrency: DATASTORE_RECOMMENDED_CONCURRENCY,
|
|
314
|
+
});
|
|
315
|
+
return keys.length;
|
|
316
|
+
}
|
|
274
317
|
async createTransaction(opt = {}) {
|
|
275
318
|
const ds = await this.ds();
|
|
276
319
|
const { readOnly } = opt;
|
|
@@ -336,11 +379,21 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
336
379
|
return o;
|
|
337
380
|
const r = {
|
|
338
381
|
...o,
|
|
339
|
-
id: this.
|
|
382
|
+
id: this.getIdFromKey(this.getDsKey(o)),
|
|
340
383
|
};
|
|
341
384
|
delete r[this.KEY];
|
|
342
385
|
return r;
|
|
343
386
|
}
|
|
387
|
+
parseDatastoreEntity(entity) {
|
|
388
|
+
const key = this.getDsKey(entity);
|
|
389
|
+
const { name, kind } = key;
|
|
390
|
+
const row = {
|
|
391
|
+
...entity,
|
|
392
|
+
id: name,
|
|
393
|
+
};
|
|
394
|
+
delete row[this.KEY];
|
|
395
|
+
return [kind, row];
|
|
396
|
+
}
|
|
344
397
|
// if key field exists on entity, it will be used as key (prevent to duplication of numeric keyed entities)
|
|
345
398
|
toDatastoreEntity(ds, kind, o, excludeFromIndexes = []) {
|
|
346
399
|
const key = this.getDsKey(o) || this.key(ds, kind, o.id);
|
|
@@ -360,7 +413,7 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
360
413
|
getDsKey(o) {
|
|
361
414
|
return o?.[this.KEY];
|
|
362
415
|
}
|
|
363
|
-
|
|
416
|
+
getIdFromKey(key) {
|
|
364
417
|
const id = key.id || key.name;
|
|
365
418
|
return id?.toString();
|
|
366
419
|
}
|
|
@@ -449,7 +502,7 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
449
502
|
logger: this.cfg.logger,
|
|
450
503
|
// not appending fingerprint here, otherwise it would just group all kinds of errors, not just Timeout errors
|
|
451
504
|
// errorData: {
|
|
452
|
-
// fingerprint:
|
|
505
|
+
// fingerprint: DATASTORE_TIMEOUT,
|
|
453
506
|
// },
|
|
454
507
|
};
|
|
455
508
|
}
|
|
@@ -508,3 +561,6 @@ export class DatastoreDBTransaction {
|
|
|
508
561
|
return await this.db.deleteByIds(table, ids, { ...opt, tx: this });
|
|
509
562
|
}
|
|
510
563
|
}
|
|
564
|
+
function idComparator(a, b) {
|
|
565
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
|
|
566
|
+
}
|
package/package.json
CHANGED
package/src/datastore.db.ts
CHANGED
|
@@ -35,7 +35,12 @@ import type { PRetryOptions } from '@naturalcycles/js-lib/promise'
|
|
|
35
35
|
import { pMap } from '@naturalcycles/js-lib/promise/pMap.js'
|
|
36
36
|
import { pRetry, pRetryFn } from '@naturalcycles/js-lib/promise/pRetry.js'
|
|
37
37
|
import { pTimeout } from '@naturalcycles/js-lib/promise/pTimeout.js'
|
|
38
|
-
import
|
|
38
|
+
import {
|
|
39
|
+
_stringMapEntries,
|
|
40
|
+
_stringMapValues,
|
|
41
|
+
type ObjectWithId,
|
|
42
|
+
type StringMap,
|
|
43
|
+
} from '@naturalcycles/js-lib/types'
|
|
39
44
|
import { boldWhite } from '@naturalcycles/nodejs-lib/colors'
|
|
40
45
|
import type { ReadableTyped } from '@naturalcycles/nodejs-lib/stream'
|
|
41
46
|
import type {
|
|
@@ -87,6 +92,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
87
92
|
override support: CommonDBSupport = {
|
|
88
93
|
...commonDBFullSupport,
|
|
89
94
|
patchByQuery: false,
|
|
95
|
+
patchById: false, // use Firestore for that
|
|
90
96
|
increment: false,
|
|
91
97
|
}
|
|
92
98
|
|
|
@@ -199,7 +205,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
199
205
|
} catch (err) {
|
|
200
206
|
if (err instanceof TimeoutError) {
|
|
201
207
|
_errorDataAppend(err, {
|
|
202
|
-
fingerprint:
|
|
208
|
+
fingerprint: DATASTORE_TIMEOUT,
|
|
203
209
|
})
|
|
204
210
|
}
|
|
205
211
|
throw err
|
|
@@ -219,10 +225,40 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
219
225
|
.map(r => this.mapId<ROW>(r))
|
|
220
226
|
// Seems like datastore .get() method doesn't return items properly sorted by input ids, so we gonna sort them here
|
|
221
227
|
// same ids are not expected here
|
|
222
|
-
.sort(
|
|
228
|
+
.sort(idComparator)
|
|
223
229
|
)
|
|
224
230
|
}
|
|
225
231
|
|
|
232
|
+
override async multiGetByIds<ROW extends ObjectWithId>(
|
|
233
|
+
map: StringMap<string[]>,
|
|
234
|
+
opt: DatastoreDBReadOptions = {},
|
|
235
|
+
): Promise<StringMap<ROW[]>> {
|
|
236
|
+
const result: StringMap<ROW[]> = {}
|
|
237
|
+
const ds = await this.ds()
|
|
238
|
+
const dsOpt = this.getRunQueryOptions(opt)
|
|
239
|
+
const keys: Key[] = []
|
|
240
|
+
for (const [table, ids] of _stringMapEntries(map)) {
|
|
241
|
+
result[table] = []
|
|
242
|
+
keys.push(...ids.map(id => this.key(ds, table, id)))
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const r = await ds.get(keys, dsOpt)
|
|
246
|
+
const rows: any[] = r[0]
|
|
247
|
+
|
|
248
|
+
rows.forEach(entity => {
|
|
249
|
+
const [kind, row] = this.parseDatastoreEntity<ROW>(entity)
|
|
250
|
+
result[kind]!.push(row)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
// Seems like datastore .get() method doesn't return items properly sorted by input ids, so we gonna sort them here
|
|
254
|
+
// same ids are not expected here
|
|
255
|
+
for (const tableRows of _stringMapValues(result)) {
|
|
256
|
+
tableRows.sort(idComparator)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return result
|
|
260
|
+
}
|
|
261
|
+
|
|
226
262
|
// getQueryKind(q: Query): string {
|
|
227
263
|
// if (!q?.kinds?.length) return '' // should never be the case, but
|
|
228
264
|
// return q.kinds[0]!
|
|
@@ -364,7 +400,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
364
400
|
} catch (err) {
|
|
365
401
|
if (err instanceof TimeoutError) {
|
|
366
402
|
_errorDataAppend(err, {
|
|
367
|
-
fingerprint:
|
|
403
|
+
fingerprint: DATASTORE_TIMEOUT,
|
|
368
404
|
})
|
|
369
405
|
}
|
|
370
406
|
|
|
@@ -378,6 +414,8 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
378
414
|
}
|
|
379
415
|
}
|
|
380
416
|
|
|
417
|
+
// not implementing multiSaveBatch, since the API does not support passing excludeFromIndexes for multiple tables
|
|
418
|
+
|
|
381
419
|
override async deleteByQuery<ROW extends ObjectWithId>(
|
|
382
420
|
q: DBQuery<ROW>,
|
|
383
421
|
opt: DatastoreDBReadOptions = {},
|
|
@@ -432,6 +470,34 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
432
470
|
return ids.length
|
|
433
471
|
}
|
|
434
472
|
|
|
473
|
+
override async multiDeleteByIds(
|
|
474
|
+
map: StringMap<string[]>,
|
|
475
|
+
opt: DatastoreDBOptions = {},
|
|
476
|
+
): Promise<number> {
|
|
477
|
+
const ds = await this.ds()
|
|
478
|
+
const keys: Key[] = []
|
|
479
|
+
for (const [table, ids] of _stringMapEntries(map)) {
|
|
480
|
+
keys.push(...ids.map(id => this.key(ds, table, id)))
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const retryOptions = this.getPRetryOptions(`DatastoreLib.multiDeleteByIds`)
|
|
484
|
+
|
|
485
|
+
await pMap(
|
|
486
|
+
_chunk(keys, MAX_ITEMS),
|
|
487
|
+
// async batch => await doDelete(batch),
|
|
488
|
+
async batchOfKeys => {
|
|
489
|
+
await pRetry(async () => {
|
|
490
|
+
await ((opt.tx as DatastoreDBTransaction)?.tx || ds).delete(batchOfKeys)
|
|
491
|
+
}, retryOptions)
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
concurrency: DATASTORE_RECOMMENDED_CONCURRENCY,
|
|
495
|
+
},
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
return keys.length
|
|
499
|
+
}
|
|
500
|
+
|
|
435
501
|
override async createTransaction(
|
|
436
502
|
opt: CommonDBTransactionOptions = {},
|
|
437
503
|
): Promise<DatastoreDBTransaction> {
|
|
@@ -508,12 +574,23 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
508
574
|
if (!o) return o
|
|
509
575
|
const r = {
|
|
510
576
|
...o,
|
|
511
|
-
id: this.
|
|
577
|
+
id: this.getIdFromKey(this.getDsKey(o)!),
|
|
512
578
|
}
|
|
513
579
|
delete r[this.KEY]
|
|
514
580
|
return r
|
|
515
581
|
}
|
|
516
582
|
|
|
583
|
+
private parseDatastoreEntity<T extends ObjectWithId>(entity: any): [kind: string, row: T] {
|
|
584
|
+
const key = this.getDsKey(entity)!
|
|
585
|
+
const { name, kind } = key
|
|
586
|
+
const row: any = {
|
|
587
|
+
...entity,
|
|
588
|
+
id: name,
|
|
589
|
+
}
|
|
590
|
+
delete row[this.KEY]
|
|
591
|
+
return [kind, row]
|
|
592
|
+
}
|
|
593
|
+
|
|
517
594
|
// if key field exists on entity, it will be used as key (prevent to duplication of numeric keyed entities)
|
|
518
595
|
private toDatastoreEntity<T extends ObjectWithId>(
|
|
519
596
|
ds: Datastore,
|
|
@@ -542,7 +619,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
542
619
|
return o?.[this.KEY]
|
|
543
620
|
}
|
|
544
621
|
|
|
545
|
-
|
|
622
|
+
private getIdFromKey(key: Key): string | undefined {
|
|
546
623
|
const id = key.id || key.name
|
|
547
624
|
return id?.toString()
|
|
548
625
|
}
|
|
@@ -638,7 +715,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
638
715
|
logger: this.cfg.logger,
|
|
639
716
|
// not appending fingerprint here, otherwise it would just group all kinds of errors, not just Timeout errors
|
|
640
717
|
// errorData: {
|
|
641
|
-
// fingerprint:
|
|
718
|
+
// fingerprint: DATASTORE_TIMEOUT,
|
|
642
719
|
// },
|
|
643
720
|
}
|
|
644
721
|
}
|
|
@@ -709,3 +786,7 @@ export class DatastoreDBTransaction implements DBTransaction {
|
|
|
709
786
|
return await this.db.deleteByIds(table, ids, { ...opt, tx: this })
|
|
710
787
|
}
|
|
711
788
|
}
|
|
789
|
+
|
|
790
|
+
function idComparator<T extends ObjectWithId>(a: T, b: T): number {
|
|
791
|
+
return a.id > b.id ? 1 : a.id < b.id ? -1 : 0
|
|
792
|
+
}
|