@naturalcycles/datastore-lib 3.38.3 → 3.39.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/DatastoreStreamReadable.d.ts +5 -4
- package/dist/DatastoreStreamReadable.js +6 -1
- package/dist/datastore.db.d.ts +14 -15
- package/dist/datastore.db.js +27 -14
- package/dist/datastore.model.d.ts +4 -2
- package/package.json +1 -1
- package/src/DatastoreStreamReadable.ts +15 -9
- package/src/datastore.db.ts +43 -22
- package/src/datastore.model.ts +4 -2
|
@@ -6,20 +6,21 @@ import type { DatastoreDBStreamOptions } from './datastore.model';
|
|
|
6
6
|
export declare class DatastoreStreamReadable<T = any> extends Readable implements ReadableTyped<T> {
|
|
7
7
|
private q;
|
|
8
8
|
private logger;
|
|
9
|
-
private originalLimit;
|
|
9
|
+
private readonly originalLimit;
|
|
10
10
|
private rowsRetrieved;
|
|
11
11
|
private endCursor?;
|
|
12
12
|
private running;
|
|
13
13
|
private done;
|
|
14
14
|
private lastQueryDone?;
|
|
15
15
|
private totalWait;
|
|
16
|
-
private table;
|
|
16
|
+
private readonly table;
|
|
17
17
|
/**
|
|
18
18
|
* Used to support maxWait
|
|
19
19
|
*/
|
|
20
20
|
private lastReadTimestamp;
|
|
21
|
-
private maxWaitInterval;
|
|
22
|
-
private opt;
|
|
21
|
+
private readonly maxWaitInterval;
|
|
22
|
+
private readonly opt;
|
|
23
|
+
private readonly dsOpt;
|
|
23
24
|
constructor(q: Query, opt: DatastoreDBStreamOptions, logger: CommonLogger);
|
|
24
25
|
private runNextQuery;
|
|
25
26
|
/**
|
|
@@ -26,6 +26,11 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
26
26
|
batchSize: 1000,
|
|
27
27
|
...opt,
|
|
28
28
|
};
|
|
29
|
+
this.dsOpt = {};
|
|
30
|
+
if (opt.readAt) {
|
|
31
|
+
// Datastore expects UnixTimestamp in milliseconds
|
|
32
|
+
this.dsOpt.readTime = opt.readAt * 1000;
|
|
33
|
+
}
|
|
29
34
|
this.originalLimit = q.limitVal;
|
|
30
35
|
this.table = q.kinds[0];
|
|
31
36
|
logger.log(`!! using experimentalCursorStream !! ${this.table}, batchSize: ${opt.batchSize}`);
|
|
@@ -71,7 +76,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
71
76
|
let info = {};
|
|
72
77
|
try {
|
|
73
78
|
await (0, js_lib_1.pRetry)(async () => {
|
|
74
|
-
const res = await q.run();
|
|
79
|
+
const res = await q.run(this.dsOpt);
|
|
75
80
|
rows = res[0];
|
|
76
81
|
info = res[1];
|
|
77
82
|
}, {
|
package/dist/datastore.db.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { Datastore, Key
|
|
1
|
+
import type { Datastore, Key } from '@google-cloud/datastore';
|
|
2
2
|
import { Transaction } from '@google-cloud/datastore';
|
|
3
|
-
import { BaseCommonDB, CommonDB, CommonDBOptions, CommonDBSaveOptions, CommonDBSupport, CommonDBTransactionOptions, DBQuery, DBTransaction, DBTransactionFn, RunQueryResult } from '@naturalcycles/db-lib';
|
|
3
|
+
import { BaseCommonDB, CommonDB, CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions, CommonDBSupport, CommonDBTransactionOptions, DBQuery, DBTransaction, DBTransactionFn, RunQueryResult } from '@naturalcycles/db-lib';
|
|
4
4
|
import { CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
|
|
5
5
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
6
|
-
import { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions,
|
|
6
|
+
import { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBReadOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePropertyStats, DatastoreStats } from './datastore.model';
|
|
7
7
|
/**
|
|
8
8
|
* Datastore API:
|
|
9
9
|
* https://googlecloudplatform.github.io/google-cloud-node/#/docs/datastore/1.0.3/datastore
|
|
@@ -22,17 +22,17 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
22
22
|
protected KEY: symbol;
|
|
23
23
|
ds(): Datastore;
|
|
24
24
|
ping(): Promise<void>;
|
|
25
|
-
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?:
|
|
26
|
-
runQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>,
|
|
27
|
-
runQueryCount<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>,
|
|
28
|
-
runDatastoreQuery
|
|
25
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: DatastoreDBReadOptions): Promise<ROW[]>;
|
|
26
|
+
runQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<RunQueryResult<ROW>>;
|
|
27
|
+
runQueryCount<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<number>;
|
|
28
|
+
private runDatastoreQuery;
|
|
29
29
|
private runQueryStream;
|
|
30
30
|
streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBStreamOptions): ReadableTyped<ROW>;
|
|
31
31
|
/**
|
|
32
32
|
* Returns saved entities with generated id/updated/created (non-mutating!)
|
|
33
33
|
*/
|
|
34
34
|
saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: DatastoreDBSaveOptions<ROW>): Promise<void>;
|
|
35
|
-
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?:
|
|
35
|
+
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<number>;
|
|
36
36
|
/**
|
|
37
37
|
* Limitation: Datastore's delete returns void, so we always return all ids here as "deleted"
|
|
38
38
|
* regardless if they were actually deleted or not.
|
|
@@ -46,12 +46,10 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
46
46
|
getStats(table: string): Promise<DatastoreStats | undefined>;
|
|
47
47
|
getStatsCount(table: string): Promise<number | undefined>;
|
|
48
48
|
getTableProperties(table: string): Promise<DatastorePropertyStats[]>;
|
|
49
|
-
mapId
|
|
50
|
-
toDatastoreEntity
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
key(kind: string, id: string | number): Key;
|
|
54
|
-
getDsKey(o: any): Key | undefined;
|
|
49
|
+
private mapId;
|
|
50
|
+
private toDatastoreEntity;
|
|
51
|
+
key(kind: string, id: string): Key;
|
|
52
|
+
private getDsKey;
|
|
55
53
|
getKey(key: Key): string | undefined;
|
|
56
54
|
createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>): Promise<void>;
|
|
57
55
|
getTables(): Promise<string[]>;
|
|
@@ -62,6 +60,7 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
62
60
|
* It may happen that transaction is already committed/rolled back, so we don't want to throw an error here.
|
|
63
61
|
*/
|
|
64
62
|
private rollback;
|
|
63
|
+
private getRunQueryOptions;
|
|
65
64
|
}
|
|
66
65
|
/**
|
|
67
66
|
* https://cloud.google.com/datastore/docs/concepts/transactions#datastore-datastore-transactional-update-nodejs
|
|
@@ -71,7 +70,7 @@ export declare class DatastoreDBTransaction implements DBTransaction {
|
|
|
71
70
|
tx: Transaction;
|
|
72
71
|
constructor(db: DatastoreDB, tx: Transaction);
|
|
73
72
|
rollback(): Promise<void>;
|
|
74
|
-
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?:
|
|
73
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: CommonDBReadOptions): Promise<ROW[]>;
|
|
75
74
|
saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
76
75
|
deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
|
|
77
76
|
}
|
package/dist/datastore.db.js
CHANGED
|
@@ -76,10 +76,11 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
76
76
|
return [];
|
|
77
77
|
const keys = ids.map(id => this.key(table, id));
|
|
78
78
|
let rows;
|
|
79
|
+
const dsOpt = this.getRunQueryOptions(opt);
|
|
79
80
|
if (this.cfg.timeout) {
|
|
80
81
|
// First try
|
|
81
82
|
try {
|
|
82
|
-
const r = await (0, js_lib_1.pTimeout)(() => (opt.tx?.tx || this.ds()).get(keys), {
|
|
83
|
+
const r = await (0, js_lib_1.pTimeout)(() => (opt.tx?.tx || this.ds()).get(keys, dsOpt), {
|
|
83
84
|
timeout: this.cfg.timeout,
|
|
84
85
|
name: `datastore.getByIds(${table})`,
|
|
85
86
|
});
|
|
@@ -92,7 +93,7 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
92
93
|
const DS = datastoreLib.Datastore;
|
|
93
94
|
this.cachedDatastore = new DS(this.cfg);
|
|
94
95
|
// Second try (will throw)
|
|
95
|
-
const r = await (0, js_lib_1.pRetry)(() => (opt.tx?.tx || this.ds()).get(keys), {
|
|
96
|
+
const r = await (0, js_lib_1.pRetry)(() => (opt.tx?.tx || this.ds()).get(keys, dsOpt), {
|
|
96
97
|
...this.getPRetryOptions(`datastore.getByIds(${table}) second try`),
|
|
97
98
|
maxAttempts: 3,
|
|
98
99
|
timeout: this.cfg.timeout,
|
|
@@ -106,7 +107,7 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
106
107
|
}
|
|
107
108
|
else {
|
|
108
109
|
rows = await (0, js_lib_1.pRetry)(async () => {
|
|
109
|
-
return (await this.ds().get(keys))[0];
|
|
110
|
+
return (await this.ds().get(keys, dsOpt))[0];
|
|
110
111
|
}, this.getPRetryOptions(`datastore.getByIds(${table})`));
|
|
111
112
|
}
|
|
112
113
|
return (rows
|
|
@@ -119,30 +120,32 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
119
120
|
// if (!q?.kinds?.length) return '' // should never be the case, but
|
|
120
121
|
// return q.kinds[0]!
|
|
121
122
|
// }
|
|
122
|
-
async runQuery(dbQuery,
|
|
123
|
+
async runQuery(dbQuery, opt = {}) {
|
|
123
124
|
const idFilter = dbQuery._filters.find(f => f.name === 'id');
|
|
124
125
|
if (idFilter) {
|
|
125
126
|
const ids = idFilter.op === '==' ? [idFilter.val] : idFilter.val;
|
|
126
127
|
return {
|
|
127
|
-
rows: await this.getByIds(dbQuery.table, ids),
|
|
128
|
+
rows: await this.getByIds(dbQuery.table, ids, opt),
|
|
128
129
|
};
|
|
129
130
|
}
|
|
130
131
|
const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery, this.ds().createQuery(dbQuery.table));
|
|
131
|
-
const
|
|
132
|
+
const dsOpt = this.getRunQueryOptions(opt);
|
|
133
|
+
const qr = await this.runDatastoreQuery(q, dsOpt);
|
|
132
134
|
// Special case when projection query didn't specify 'id'
|
|
133
135
|
if (dbQuery._selectedFieldNames && !dbQuery._selectedFieldNames.includes('id')) {
|
|
134
136
|
qr.rows = qr.rows.map(r => (0, js_lib_1._omit)(r, ['id']));
|
|
135
137
|
}
|
|
136
138
|
return qr;
|
|
137
139
|
}
|
|
138
|
-
async runQueryCount(dbQuery,
|
|
140
|
+
async runQueryCount(dbQuery, opt = {}) {
|
|
139
141
|
const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery.select([]), this.ds().createQuery(dbQuery.table));
|
|
140
142
|
const aq = this.ds().createAggregationQuery(q).count('count');
|
|
141
|
-
const
|
|
143
|
+
const dsOpt = this.getRunQueryOptions(opt);
|
|
144
|
+
const [entities] = await this.ds().runAggregationQuery(aq, dsOpt);
|
|
142
145
|
return entities[0]?.count;
|
|
143
146
|
}
|
|
144
|
-
async runDatastoreQuery(q) {
|
|
145
|
-
const [entities, queryResult] = await this.ds().runQuery(q);
|
|
147
|
+
async runDatastoreQuery(q, dsOpt) {
|
|
148
|
+
const [entities, queryResult] = await this.ds().runQuery(q, dsOpt);
|
|
146
149
|
const rows = entities.map(e => this.mapId(e));
|
|
147
150
|
return {
|
|
148
151
|
...queryResult,
|
|
@@ -154,9 +157,10 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
154
157
|
...this.cfg.streamOptions,
|
|
155
158
|
..._opt,
|
|
156
159
|
};
|
|
160
|
+
const dsOpt = this.getRunQueryOptions(opt);
|
|
157
161
|
return (opt.experimentalCursorStream
|
|
158
162
|
? new DatastoreStreamReadable_1.DatastoreStreamReadable(q, opt, (0, js_lib_1.commonLoggerMinLevel)(this.cfg.logger, opt.debug ? 'log' : 'warn'))
|
|
159
|
-
: this.ds().runQueryStream(q)).map(chunk => this.mapId(chunk));
|
|
163
|
+
: this.ds().runQueryStream(q, dsOpt)).map(chunk => this.mapId(chunk));
|
|
160
164
|
}
|
|
161
165
|
streamQuery(dbQuery, opt) {
|
|
162
166
|
const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery, this.ds().createQuery(dbQuery.table));
|
|
@@ -190,14 +194,15 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
190
194
|
throw err;
|
|
191
195
|
}
|
|
192
196
|
}
|
|
193
|
-
async deleteByQuery(q, opt) {
|
|
197
|
+
async deleteByQuery(q, opt = {}) {
|
|
194
198
|
const idFilter = q._filters.find(f => f.name === 'id');
|
|
195
199
|
if (idFilter) {
|
|
196
200
|
const ids = idFilter.op === '==' ? [idFilter.val] : idFilter.val;
|
|
197
201
|
return await this.deleteByIds(q.table, ids, opt);
|
|
198
202
|
}
|
|
199
203
|
const datastoreQuery = (0, query_util_1.dbQueryToDatastoreQuery)(q.select([]), this.ds().createQuery(q.table));
|
|
200
|
-
const
|
|
204
|
+
const dsOpt = this.getRunQueryOptions(opt);
|
|
205
|
+
const { rows } = await this.runDatastoreQuery(datastoreQuery, dsOpt);
|
|
201
206
|
return await this.deleteByIds(q.table, rows.map(obj => obj.id), opt);
|
|
202
207
|
}
|
|
203
208
|
/**
|
|
@@ -282,7 +287,7 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
282
287
|
}
|
|
283
288
|
key(kind, id) {
|
|
284
289
|
(0, js_lib_1._assert)(id, `Cannot save "${kind}" entity without "id"`);
|
|
285
|
-
return this.ds().key([kind,
|
|
290
|
+
return this.ds().key([kind, id]);
|
|
286
291
|
}
|
|
287
292
|
getDsKey(o) {
|
|
288
293
|
return o?.[this.KEY];
|
|
@@ -392,6 +397,14 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
392
397
|
this.cfg.logger.error(err);
|
|
393
398
|
}
|
|
394
399
|
}
|
|
400
|
+
getRunQueryOptions(opt) {
|
|
401
|
+
if (!opt.readAt)
|
|
402
|
+
return {};
|
|
403
|
+
return {
|
|
404
|
+
// Datastore expects UnixTimestamp in milliseconds
|
|
405
|
+
readTime: opt.readAt * 1000,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
395
408
|
}
|
|
396
409
|
exports.DatastoreDB = DatastoreDB;
|
|
397
410
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DatastoreOptions, Key } from '@google-cloud/datastore';
|
|
2
|
-
import { CommonDBOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib';
|
|
2
|
+
import { CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib';
|
|
3
3
|
import { CommonLogger, NumberOfSeconds, ObjectWithId } from '@naturalcycles/js-lib';
|
|
4
4
|
export interface DatastorePayload<T = any> {
|
|
5
5
|
key: Key;
|
|
@@ -47,7 +47,7 @@ export interface DatastoreCredentials {
|
|
|
47
47
|
client_secret?: string;
|
|
48
48
|
refresh_token?: string;
|
|
49
49
|
}
|
|
50
|
-
export interface DatastoreDBStreamOptions extends
|
|
50
|
+
export interface DatastoreDBStreamOptions extends DatastoreDBReadOptions {
|
|
51
51
|
/**
|
|
52
52
|
* Set to `true` to stream via experimental "cursor-query based stream".
|
|
53
53
|
*
|
|
@@ -101,6 +101,8 @@ export interface DatastoreDBStreamOptions extends DatastoreDBOptions {
|
|
|
101
101
|
}
|
|
102
102
|
export interface DatastoreDBOptions extends CommonDBOptions {
|
|
103
103
|
}
|
|
104
|
+
export interface DatastoreDBReadOptions extends CommonDBReadOptions {
|
|
105
|
+
}
|
|
104
106
|
export interface DatastoreDBSaveOptions<ROW extends ObjectWithId> extends CommonDBSaveOptions<ROW> {
|
|
105
107
|
}
|
|
106
108
|
export interface DatastoreStats {
|
package/package.json
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { Readable } from 'node:stream'
|
|
2
2
|
import { Query } from '@google-cloud/datastore'
|
|
3
|
-
import type { RunQueryInfo } from '@google-cloud/datastore/build/src/query'
|
|
4
|
-
import { _ms, CommonLogger, pRetry,
|
|
3
|
+
import type { RunQueryInfo, RunQueryOptions } from '@google-cloud/datastore/build/src/query'
|
|
4
|
+
import { _ms, CommonLogger, pRetry, UnixTimestampMillis } from '@naturalcycles/js-lib'
|
|
5
5
|
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
6
6
|
import type { DatastoreDBStreamOptions } from './datastore.model'
|
|
7
7
|
|
|
8
8
|
export class DatastoreStreamReadable<T = any> extends Readable implements ReadableTyped<T> {
|
|
9
|
-
private originalLimit: number
|
|
9
|
+
private readonly originalLimit: number
|
|
10
10
|
private rowsRetrieved = 0
|
|
11
11
|
private endCursor?: string
|
|
12
12
|
private running = false
|
|
13
13
|
private done = false
|
|
14
14
|
private lastQueryDone?: number
|
|
15
15
|
private totalWait = 0
|
|
16
|
-
private table: string
|
|
16
|
+
private readonly table: string
|
|
17
17
|
/**
|
|
18
18
|
* Used to support maxWait
|
|
19
19
|
*/
|
|
20
|
-
private lastReadTimestamp
|
|
21
|
-
private maxWaitInterval: NodeJS.Timeout | undefined
|
|
20
|
+
private lastReadTimestamp = 0 as UnixTimestampMillis
|
|
21
|
+
private readonly maxWaitInterval: NodeJS.Timeout | undefined
|
|
22
22
|
|
|
23
|
-
private opt: DatastoreDBStreamOptions & { batchSize: number }
|
|
23
|
+
private readonly opt: DatastoreDBStreamOptions & { batchSize: number }
|
|
24
|
+
private readonly dsOpt: RunQueryOptions
|
|
24
25
|
|
|
25
26
|
constructor(
|
|
26
27
|
private q: Query,
|
|
@@ -34,6 +35,11 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
34
35
|
batchSize: 1000,
|
|
35
36
|
...opt,
|
|
36
37
|
}
|
|
38
|
+
this.dsOpt = {}
|
|
39
|
+
if (opt.readAt) {
|
|
40
|
+
// Datastore expects UnixTimestamp in milliseconds
|
|
41
|
+
this.dsOpt.readTime = opt.readAt * 1000
|
|
42
|
+
}
|
|
37
43
|
|
|
38
44
|
this.originalLimit = q.limitVal
|
|
39
45
|
this.table = q.kinds[0]!
|
|
@@ -99,7 +105,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
99
105
|
try {
|
|
100
106
|
await pRetry(
|
|
101
107
|
async () => {
|
|
102
|
-
const res = await q.run()
|
|
108
|
+
const res = await q.run(this.dsOpt)
|
|
103
109
|
rows = res[0]
|
|
104
110
|
info = res[1]
|
|
105
111
|
},
|
|
@@ -181,7 +187,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
181
187
|
count = 0
|
|
182
188
|
|
|
183
189
|
override _read(): void {
|
|
184
|
-
this.lastReadTimestamp = Date.now()
|
|
190
|
+
this.lastReadTimestamp = Date.now() as UnixTimestampMillis
|
|
185
191
|
|
|
186
192
|
// console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
|
|
187
193
|
this.count++
|
package/src/datastore.db.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { Datastore, Key, Query } from '@google-cloud/datastore'
|
|
2
2
|
import { PropertyFilter, Transaction } from '@google-cloud/datastore'
|
|
3
|
+
import { type RunQueryOptions } from '@google-cloud/datastore/build/src/query'
|
|
3
4
|
import {
|
|
4
5
|
BaseCommonDB,
|
|
5
6
|
CommonDB,
|
|
6
7
|
commonDBFullSupport,
|
|
7
8
|
CommonDBOptions,
|
|
9
|
+
CommonDBReadOptions,
|
|
8
10
|
CommonDBSaveMethod,
|
|
9
11
|
CommonDBSaveOptions,
|
|
10
12
|
CommonDBSupport,
|
|
@@ -38,6 +40,7 @@ import { boldWhite, ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
|
38
40
|
import {
|
|
39
41
|
DatastoreDBCfg,
|
|
40
42
|
DatastoreDBOptions,
|
|
43
|
+
DatastoreDBReadOptions,
|
|
41
44
|
DatastoreDBSaveOptions,
|
|
42
45
|
DatastoreDBStreamOptions,
|
|
43
46
|
DatastorePayload,
|
|
@@ -138,17 +141,19 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
138
141
|
override async getByIds<ROW extends ObjectWithId>(
|
|
139
142
|
table: string,
|
|
140
143
|
ids: string[],
|
|
141
|
-
opt:
|
|
144
|
+
opt: DatastoreDBReadOptions = {},
|
|
142
145
|
): Promise<ROW[]> {
|
|
143
146
|
if (!ids.length) return []
|
|
144
147
|
const keys = ids.map(id => this.key(table, id))
|
|
145
148
|
let rows: any[]
|
|
146
149
|
|
|
150
|
+
const dsOpt = this.getRunQueryOptions(opt)
|
|
151
|
+
|
|
147
152
|
if (this.cfg.timeout) {
|
|
148
153
|
// First try
|
|
149
154
|
try {
|
|
150
155
|
const r = await pTimeout(
|
|
151
|
-
() => ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).get(keys),
|
|
156
|
+
() => ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).get(keys, dsOpt),
|
|
152
157
|
{
|
|
153
158
|
timeout: this.cfg.timeout,
|
|
154
159
|
name: `datastore.getByIds(${table})`,
|
|
@@ -165,7 +170,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
165
170
|
|
|
166
171
|
// Second try (will throw)
|
|
167
172
|
const r = await pRetry(
|
|
168
|
-
() => ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).get(keys),
|
|
173
|
+
() => ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).get(keys, dsOpt),
|
|
169
174
|
{
|
|
170
175
|
...this.getPRetryOptions(`datastore.getByIds(${table}) second try`),
|
|
171
176
|
maxAttempts: 3,
|
|
@@ -181,7 +186,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
181
186
|
} else {
|
|
182
187
|
rows = await pRetry(
|
|
183
188
|
async () => {
|
|
184
|
-
return (await this.ds().get(keys))[0]
|
|
189
|
+
return (await this.ds().get(keys, dsOpt))[0]
|
|
185
190
|
},
|
|
186
191
|
this.getPRetryOptions(`datastore.getByIds(${table})`),
|
|
187
192
|
)
|
|
@@ -203,19 +208,20 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
203
208
|
|
|
204
209
|
override async runQuery<ROW extends ObjectWithId>(
|
|
205
210
|
dbQuery: DBQuery<ROW>,
|
|
206
|
-
|
|
211
|
+
opt: DatastoreDBReadOptions = {},
|
|
207
212
|
): Promise<RunQueryResult<ROW>> {
|
|
208
213
|
const idFilter = dbQuery._filters.find(f => f.name === 'id')
|
|
209
214
|
if (idFilter) {
|
|
210
215
|
const ids: string[] = idFilter.op === '==' ? [idFilter.val] : idFilter.val
|
|
211
216
|
|
|
212
217
|
return {
|
|
213
|
-
rows: await this.getByIds(dbQuery.table, ids),
|
|
218
|
+
rows: await this.getByIds(dbQuery.table, ids, opt),
|
|
214
219
|
}
|
|
215
220
|
}
|
|
216
221
|
|
|
217
222
|
const q = dbQueryToDatastoreQuery(dbQuery, this.ds().createQuery(dbQuery.table))
|
|
218
|
-
const
|
|
223
|
+
const dsOpt = this.getRunQueryOptions(opt)
|
|
224
|
+
const qr = await this.runDatastoreQuery<ROW>(q, dsOpt)
|
|
219
225
|
|
|
220
226
|
// Special case when projection query didn't specify 'id'
|
|
221
227
|
if (dbQuery._selectedFieldNames && !dbQuery._selectedFieldNames.includes('id')) {
|
|
@@ -227,16 +233,20 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
227
233
|
|
|
228
234
|
override async runQueryCount<ROW extends ObjectWithId>(
|
|
229
235
|
dbQuery: DBQuery<ROW>,
|
|
230
|
-
|
|
236
|
+
opt: DatastoreDBReadOptions = {},
|
|
231
237
|
): Promise<number> {
|
|
232
238
|
const q = dbQueryToDatastoreQuery(dbQuery.select([]), this.ds().createQuery(dbQuery.table))
|
|
233
239
|
const aq = this.ds().createAggregationQuery(q).count('count')
|
|
234
|
-
const
|
|
240
|
+
const dsOpt = this.getRunQueryOptions(opt)
|
|
241
|
+
const [entities] = await this.ds().runAggregationQuery(aq, dsOpt)
|
|
235
242
|
return entities[0]?.count
|
|
236
243
|
}
|
|
237
244
|
|
|
238
|
-
async runDatastoreQuery<ROW extends ObjectWithId>(
|
|
239
|
-
|
|
245
|
+
private async runDatastoreQuery<ROW extends ObjectWithId>(
|
|
246
|
+
q: Query,
|
|
247
|
+
dsOpt: RunQueryOptions,
|
|
248
|
+
): Promise<RunQueryResult<ROW>> {
|
|
249
|
+
const [entities, queryResult] = await this.ds().runQuery(q, dsOpt)
|
|
240
250
|
|
|
241
251
|
const rows = entities.map(e => this.mapId<ROW>(e))
|
|
242
252
|
|
|
@@ -254,6 +264,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
254
264
|
...this.cfg.streamOptions,
|
|
255
265
|
..._opt,
|
|
256
266
|
}
|
|
267
|
+
const dsOpt = this.getRunQueryOptions(opt)
|
|
257
268
|
|
|
258
269
|
return (
|
|
259
270
|
opt.experimentalCursorStream
|
|
@@ -262,7 +273,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
262
273
|
opt,
|
|
263
274
|
commonLoggerMinLevel(this.cfg.logger, opt.debug ? 'log' : 'warn'),
|
|
264
275
|
)
|
|
265
|
-
: (this.ds().runQueryStream(q) as ReadableTyped<ROW>)
|
|
276
|
+
: (this.ds().runQueryStream(q, dsOpt) as ReadableTyped<ROW>)
|
|
266
277
|
).map(chunk => this.mapId(chunk))
|
|
267
278
|
}
|
|
268
279
|
|
|
@@ -320,7 +331,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
320
331
|
|
|
321
332
|
override async deleteByQuery<ROW extends ObjectWithId>(
|
|
322
333
|
q: DBQuery<ROW>,
|
|
323
|
-
opt
|
|
334
|
+
opt: DatastoreDBReadOptions = {},
|
|
324
335
|
): Promise<number> {
|
|
325
336
|
const idFilter = q._filters.find(f => f.name === 'id')
|
|
326
337
|
if (idFilter) {
|
|
@@ -329,7 +340,8 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
329
340
|
}
|
|
330
341
|
|
|
331
342
|
const datastoreQuery = dbQueryToDatastoreQuery(q.select([]), this.ds().createQuery(q.table))
|
|
332
|
-
const
|
|
343
|
+
const dsOpt = this.getRunQueryOptions(opt)
|
|
344
|
+
const { rows } = await this.runDatastoreQuery<ROW>(datastoreQuery, dsOpt)
|
|
333
345
|
return await this.deleteByIds(
|
|
334
346
|
q.table,
|
|
335
347
|
rows.map(obj => obj.id),
|
|
@@ -413,7 +425,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
413
425
|
return stats
|
|
414
426
|
}
|
|
415
427
|
|
|
416
|
-
mapId<T extends ObjectWithId>(o: any, preserveKey = false): T {
|
|
428
|
+
private mapId<T extends ObjectWithId>(o: any, preserveKey = false): T {
|
|
417
429
|
if (!o) return o
|
|
418
430
|
const r = {
|
|
419
431
|
...o,
|
|
@@ -424,12 +436,12 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
424
436
|
}
|
|
425
437
|
|
|
426
438
|
// if key field exists on entity, it will be used as key (prevent to duplication of numeric keyed entities)
|
|
427
|
-
toDatastoreEntity<T
|
|
439
|
+
private toDatastoreEntity<T extends ObjectWithId>(
|
|
428
440
|
kind: string,
|
|
429
|
-
o: T
|
|
441
|
+
o: T,
|
|
430
442
|
excludeFromIndexes: string[] = [],
|
|
431
443
|
): DatastorePayload<T> {
|
|
432
|
-
const key = this.getDsKey(o) || this.key(kind, o.id
|
|
444
|
+
const key = this.getDsKey(o) || this.key(kind, o.id)
|
|
433
445
|
const data = Object.assign({}, o) as any
|
|
434
446
|
delete data.id
|
|
435
447
|
delete data[this.KEY]
|
|
@@ -441,12 +453,12 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
441
453
|
}
|
|
442
454
|
}
|
|
443
455
|
|
|
444
|
-
key(kind: string, id: string
|
|
456
|
+
key(kind: string, id: string): Key {
|
|
445
457
|
_assert(id, `Cannot save "${kind}" entity without "id"`)
|
|
446
|
-
return this.ds().key([kind,
|
|
458
|
+
return this.ds().key([kind, id])
|
|
447
459
|
}
|
|
448
460
|
|
|
449
|
-
getDsKey(o: any): Key | undefined {
|
|
461
|
+
private getDsKey(o: any): Key | undefined {
|
|
450
462
|
return o?.[this.KEY]
|
|
451
463
|
}
|
|
452
464
|
|
|
@@ -562,6 +574,15 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
562
574
|
this.cfg.logger.error(err)
|
|
563
575
|
}
|
|
564
576
|
}
|
|
577
|
+
|
|
578
|
+
private getRunQueryOptions(opt: DatastoreDBReadOptions): RunQueryOptions {
|
|
579
|
+
if (!opt.readAt) return {}
|
|
580
|
+
|
|
581
|
+
return {
|
|
582
|
+
// Datastore expects UnixTimestamp in milliseconds
|
|
583
|
+
readTime: opt.readAt * 1000,
|
|
584
|
+
}
|
|
585
|
+
}
|
|
565
586
|
}
|
|
566
587
|
|
|
567
588
|
/**
|
|
@@ -585,7 +606,7 @@ export class DatastoreDBTransaction implements DBTransaction {
|
|
|
585
606
|
async getByIds<ROW extends ObjectWithId>(
|
|
586
607
|
table: string,
|
|
587
608
|
ids: string[],
|
|
588
|
-
opt?:
|
|
609
|
+
opt?: CommonDBReadOptions,
|
|
589
610
|
): Promise<ROW[]> {
|
|
590
611
|
return await this.db.getByIds(table, ids, { ...opt, tx: this })
|
|
591
612
|
}
|
package/src/datastore.model.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DatastoreOptions, Key } from '@google-cloud/datastore'
|
|
2
|
-
import { CommonDBOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib'
|
|
2
|
+
import { CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib'
|
|
3
3
|
import { CommonLogger, NumberOfSeconds, ObjectWithId } from '@naturalcycles/js-lib'
|
|
4
4
|
|
|
5
5
|
export interface DatastorePayload<T = any> {
|
|
@@ -56,7 +56,7 @@ export interface DatastoreCredentials {
|
|
|
56
56
|
refresh_token?: string
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export interface DatastoreDBStreamOptions extends
|
|
59
|
+
export interface DatastoreDBStreamOptions extends DatastoreDBReadOptions {
|
|
60
60
|
/**
|
|
61
61
|
* Set to `true` to stream via experimental "cursor-query based stream".
|
|
62
62
|
*
|
|
@@ -116,6 +116,8 @@ export interface DatastoreDBStreamOptions extends DatastoreDBOptions {
|
|
|
116
116
|
|
|
117
117
|
export interface DatastoreDBOptions extends CommonDBOptions {}
|
|
118
118
|
|
|
119
|
+
export interface DatastoreDBReadOptions extends CommonDBReadOptions {}
|
|
120
|
+
|
|
119
121
|
export interface DatastoreDBSaveOptions<ROW extends ObjectWithId>
|
|
120
122
|
extends CommonDBSaveOptions<ROW> {}
|
|
121
123
|
|