@naturalcycles/datastore-lib 3.39.3 → 4.0.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.
@@ -1,8 +1,8 @@
1
1
  import { Readable } from 'node:stream';
2
- import { Query } from '@google-cloud/datastore';
3
- import { CommonLogger } from '@naturalcycles/js-lib';
2
+ import type { Query } from '@google-cloud/datastore';
3
+ import type { CommonLogger } from '@naturalcycles/js-lib';
4
4
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
5
- import type { DatastoreDBStreamOptions } from './datastore.model';
5
+ import type { DatastoreDBStreamOptions } from './datastore.model.js';
6
6
  export declare class DatastoreStreamReadable<T = any> extends Readable implements ReadableTyped<T> {
7
7
  private q;
8
8
  private logger;
@@ -1,26 +1,27 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatastoreStreamReadable = void 0;
4
- const node_stream_1 = require("node:stream");
5
- const js_lib_1 = require("@naturalcycles/js-lib");
6
- class DatastoreStreamReadable extends node_stream_1.Readable {
1
+ import { Readable } from 'node:stream';
2
+ import { _ms, pRetry } from '@naturalcycles/js-lib';
3
+ export class DatastoreStreamReadable extends Readable {
4
+ q;
5
+ logger;
6
+ originalLimit;
7
+ rowsRetrieved = 0;
8
+ endCursor;
9
+ running = false;
10
+ done = false;
11
+ lastQueryDone;
12
+ totalWait = 0;
13
+ table;
14
+ /**
15
+ * Used to support maxWait
16
+ */
17
+ lastReadTimestamp = 0;
18
+ maxWaitInterval;
19
+ opt;
20
+ dsOpt;
7
21
  constructor(q, opt, logger) {
8
22
  super({ objectMode: true });
9
23
  this.q = q;
10
24
  this.logger = logger;
11
- this.rowsRetrieved = 0;
12
- this.running = false;
13
- this.done = false;
14
- this.totalWait = 0;
15
- /**
16
- * Used to support maxWait
17
- */
18
- this.lastReadTimestamp = 0;
19
- /**
20
- * Counts how many times _read was called.
21
- * For debugging.
22
- */
23
- this.count = 0;
24
25
  this.opt = {
25
26
  rssLimitMB: 1000,
26
27
  batchSize: 1000,
@@ -75,7 +76,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
75
76
  let rows = [];
76
77
  let info = {};
77
78
  try {
78
- await (0, js_lib_1.pRetry)(async () => {
79
+ await pRetry(async () => {
79
80
  const res = await q.run(this.dsOpt);
80
81
  rows = res[0];
81
82
  info = res[1];
@@ -98,9 +99,9 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
98
99
  return;
99
100
  }
100
101
  this.rowsRetrieved += rows.length;
101
- this.logger.log(`${this.table} got ${rows.length} rows, ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`, info.moreResults);
102
+ this.logger.log(`${this.table} got ${rows.length} rows, ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(this.totalWait)}`, info.moreResults);
102
103
  if (!rows.length) {
103
- this.logger.warn(`${this.table} got 0 rows, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`, info.moreResults);
104
+ this.logger.warn(`${this.table} got 0 rows, totalWait: ${_ms(this.totalWait)}`, info.moreResults);
104
105
  }
105
106
  this.endCursor = info.endCursor;
106
107
  this.running = false; // ready to take more _reads
@@ -109,7 +110,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
109
110
  if (!info.endCursor ||
110
111
  info.moreResults === 'NO_MORE_RESULTS' ||
111
112
  (this.originalLimit && this.rowsRetrieved >= this.originalLimit)) {
112
- this.logger.log(`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`);
113
+ this.logger.log(`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(this.totalWait)}`);
113
114
  this.push(null);
114
115
  this.done = true;
115
116
  clearInterval(this.maxWaitInterval);
@@ -128,6 +129,11 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
128
129
  }
129
130
  }
130
131
  }
132
+ /**
133
+ * Counts how many times _read was called.
134
+ * For debugging.
135
+ */
136
+ count = 0;
131
137
  _read() {
132
138
  this.lastReadTimestamp = Date.now();
133
139
  // console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
@@ -147,4 +153,3 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
147
153
  }
148
154
  }
149
155
  }
150
- exports.DatastoreStreamReadable = DatastoreStreamReadable;
@@ -1,9 +1,9 @@
1
- import type { Datastore, Key } from '@google-cloud/datastore';
2
- import { Transaction } from '@google-cloud/datastore';
3
- import { BaseCommonDB, CommonDB, CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions, CommonDBSupport, CommonDBTransactionOptions, DBQuery, DBTransaction, DBTransactionFn, RunQueryResult } from '@naturalcycles/db-lib';
4
- import { CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
5
- import { ReadableTyped } from '@naturalcycles/nodejs-lib';
6
- import { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBReadOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePropertyStats, DatastoreStats } from './datastore.model';
1
+ import type { Datastore, Key, Transaction } from '@google-cloud/datastore';
2
+ import type { CommonDB, CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions, CommonDBSupport, CommonDBTransactionOptions, DBQuery, DBTransaction, DBTransactionFn, RunQueryResult } from '@naturalcycles/db-lib';
3
+ import { BaseCommonDB } from '@naturalcycles/db-lib';
4
+ import type { CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
5
+ import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
6
+ import type { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBReadOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePropertyStats, DatastoreStats } from './datastore.model.js';
7
7
  /**
8
8
  * Datastore API:
9
9
  * https://googlecloudplatform.github.io/google-cloud-node/#/docs/datastore/1.0.3/datastore
@@ -20,14 +20,15 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
20
20
  * Datastore.KEY
21
21
  */
22
22
  protected KEY: symbol;
23
- ds(): Datastore;
23
+ ds(): Promise<Datastore>;
24
+ private getPropertyFilter;
25
+ private getDatastoreLib;
24
26
  ping(): Promise<void>;
25
27
  getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: DatastoreDBReadOptions): Promise<ROW[]>;
26
28
  runQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<RunQueryResult<ROW>>;
27
29
  runQueryCount<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<number>;
28
30
  private runDatastoreQuery;
29
- private runQueryStream;
30
- streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBStreamOptions): ReadableTyped<ROW>;
31
+ streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, _opt?: DatastoreDBStreamOptions): ReadableTyped<ROW>;
31
32
  /**
32
33
  * Returns saved entities with generated id/updated/created (non-mutating!)
33
34
  */
@@ -48,7 +49,7 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
48
49
  getTableProperties(table: string): Promise<DatastorePropertyStats[]>;
49
50
  private mapId;
50
51
  private toDatastoreEntity;
51
- key(kind: string, id: string): Key;
52
+ key(ds: Datastore, kind: string, id: string): Key;
52
53
  private getDsKey;
53
54
  getKey(key: Key): string | undefined;
54
55
  createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>): Promise<void>;
@@ -1,13 +1,10 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatastoreDBTransaction = exports.DatastoreDB = void 0;
4
- const datastore_1 = require("@google-cloud/datastore");
5
- const db_lib_1 = require("@naturalcycles/db-lib");
6
- const js_lib_1 = require("@naturalcycles/js-lib");
7
- const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
8
- const datastore_model_1 = require("./datastore.model");
9
- const DatastoreStreamReadable_1 = require("./DatastoreStreamReadable");
10
- const query_util_1 = require("./query.util");
1
+ import { Transform } from 'node:stream';
2
+ import { BaseCommonDB, commonDBFullSupport } from '@naturalcycles/db-lib';
3
+ import { _assert, _chunk, _errorDataAppend, _omit, commonLoggerMinLevel, pMap, pRetry, pRetryFn, pTimeout, TimeoutError, } from '@naturalcycles/js-lib';
4
+ import { boldWhite } from '@naturalcycles/nodejs-lib';
5
+ import { DatastoreType } from './datastore.model.js';
6
+ import { DatastoreStreamReadable } from './DatastoreStreamReadable.js';
7
+ import { dbQueryToDatastoreQuery } from './query.util.js';
11
8
  // Datastore (also Firestore and other Google APIs) supports max 500 of items when saving/deleting, etc.
12
9
  const MAX_ITEMS = 500;
13
10
  // It's an empyrical value, but anything less than infinity is better than infinity
@@ -33,29 +30,33 @@ const methodMap = {
33
30
  * https://googlecloudplatform.github.io/google-cloud-node/#/docs/datastore/1.0.3/datastore
34
31
  * https://cloud.google.com/datastore/docs/datastore-api-tutorial
35
32
  */
36
- class DatastoreDB extends db_lib_1.BaseCommonDB {
33
+ export class DatastoreDB extends BaseCommonDB {
34
+ support = {
35
+ ...commonDBFullSupport,
36
+ patchByQuery: false,
37
+ increment: false,
38
+ };
37
39
  constructor(cfg = {}) {
38
40
  super();
39
- this.support = {
40
- ...db_lib_1.commonDBFullSupport,
41
- patchByQuery: false,
42
- increment: false,
43
- };
44
41
  this.cfg = {
45
42
  logger: console,
46
43
  ...cfg,
47
44
  };
48
45
  }
46
+ cfg;
47
+ cachedDatastore;
48
+ /**
49
+ * Datastore.KEY
50
+ */
51
+ KEY;
49
52
  // @memo() // not used to be able to connect to many DBs in the same server instance
50
- ds() {
53
+ async ds() {
51
54
  if (!this.cachedDatastore) {
52
- (0, js_lib_1._assert)(process.env['APP_ENV'] !== 'test', 'DatastoreDB cannot be used in Test env, please use InMemoryDB');
53
- // Lazy-loading
54
- const datastoreLib = require('@google-cloud/datastore');
55
- const DS = datastoreLib.Datastore;
55
+ _assert(process.env['APP_ENV'] !== 'test', 'DatastoreDB cannot be used in Test env, please use InMemoryDB');
56
+ const DS = (await this.getDatastoreLib()).Datastore;
56
57
  this.cfg.projectId ||= this.cfg.credentials?.project_id || process.env['GOOGLE_CLOUD_PROJECT'];
57
58
  if (this.cfg.projectId) {
58
- this.cfg.logger.log(`DatastoreDB connected to ${(0, nodejs_lib_1.boldWhite)(this.cfg.projectId)}`);
59
+ this.cfg.logger.log(`DatastoreDB connected to ${boldWhite(this.cfg.projectId)}`);
59
60
  }
60
61
  else if (process.env['GOOGLE_APPLICATION_CREDENTIALS']) {
61
62
  this.cfg.logger.log(`DatastoreDB connected via GOOGLE_APPLICATION_CREDENTIALS`);
@@ -68,37 +69,46 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
68
69
  }
69
70
  return this.cachedDatastore;
70
71
  }
72
+ async getPropertyFilter() {
73
+ return (await this.getDatastoreLib()).PropertyFilter;
74
+ }
75
+ async getDatastoreLib() {
76
+ // Lazy-loading
77
+ const { default: lib } = await import('@google-cloud/datastore');
78
+ return lib;
79
+ }
71
80
  async ping() {
72
81
  await this.getAllStats();
73
82
  }
74
83
  async getByIds(table, ids, opt = {}) {
75
84
  if (!ids.length)
76
85
  return [];
77
- const keys = ids.map(id => this.key(table, id));
86
+ let ds = await this.ds();
87
+ const keys = ids.map(id => this.key(ds, table, id));
78
88
  let rows;
79
89
  const dsOpt = this.getRunQueryOptions(opt);
80
90
  if (this.cfg.timeout) {
81
91
  // First try
82
92
  try {
83
- const r = await (0, js_lib_1.pTimeout)(() => (opt.tx?.tx || this.ds()).get(keys, dsOpt), {
93
+ const r = await pTimeout(() => (opt.tx?.tx || ds).get(keys, dsOpt), {
84
94
  timeout: this.cfg.timeout,
85
95
  name: `datastore.getByIds(${table})`,
86
96
  });
87
97
  rows = r[0];
88
98
  }
89
99
  catch (err) {
90
- if (!(err instanceof js_lib_1.TimeoutError)) {
100
+ if (!(err instanceof TimeoutError)) {
91
101
  // Not a timeout error, re-throw
92
102
  throw err;
93
103
  }
94
104
  this.cfg.logger.log('datastore recreated on error');
95
105
  // This is to debug "GCP Datastore Timeout issue"
96
- const datastoreLib = require('@google-cloud/datastore');
106
+ const datastoreLib = await this.getDatastoreLib();
97
107
  const DS = datastoreLib.Datastore;
98
- this.cachedDatastore = new DS(this.cfg);
108
+ ds = this.cachedDatastore = new DS(this.cfg);
99
109
  // Second try (will throw)
100
110
  try {
101
- const r = await (0, js_lib_1.pRetry)(() => (opt.tx?.tx || this.ds()).get(keys, dsOpt), {
111
+ const r = await pRetry(() => (opt.tx?.tx || ds).get(keys, dsOpt), {
102
112
  ...this.getPRetryOptions(`datastore.getByIds(${table}) second try`),
103
113
  maxAttempts: 3,
104
114
  timeout: this.cfg.timeout,
@@ -106,8 +116,8 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
106
116
  rows = r[0];
107
117
  }
108
118
  catch (err) {
109
- if (err instanceof js_lib_1.TimeoutError) {
110
- (0, js_lib_1._errorDataAppend)(err, {
119
+ if (err instanceof TimeoutError) {
120
+ _errorDataAppend(err, {
111
121
  fingerprint: [DATASTORE_TIMEOUT],
112
122
  });
113
123
  }
@@ -116,8 +126,8 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
116
126
  }
117
127
  }
118
128
  else {
119
- rows = await (0, js_lib_1.pRetry)(async () => {
120
- return (await this.ds().get(keys, dsOpt))[0];
129
+ rows = await pRetry(async () => {
130
+ return (await ds.get(keys, dsOpt))[0];
121
131
  }, this.getPRetryOptions(`datastore.getByIds(${table})`));
122
132
  }
123
133
  return (rows
@@ -138,69 +148,80 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
138
148
  rows: await this.getByIds(dbQuery.table, ids, opt),
139
149
  };
140
150
  }
141
- const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery, this.ds().createQuery(dbQuery.table));
151
+ const ds = await this.ds();
152
+ const q = dbQueryToDatastoreQuery(dbQuery, ds.createQuery(dbQuery.table), await this.getPropertyFilter());
142
153
  const dsOpt = this.getRunQueryOptions(opt);
143
154
  const qr = await this.runDatastoreQuery(q, dsOpt);
144
155
  // Special case when projection query didn't specify 'id'
145
156
  if (dbQuery._selectedFieldNames && !dbQuery._selectedFieldNames.includes('id')) {
146
- qr.rows = qr.rows.map(r => (0, js_lib_1._omit)(r, ['id']));
157
+ qr.rows = qr.rows.map(r => _omit(r, ['id']));
147
158
  }
148
159
  return qr;
149
160
  }
150
161
  async runQueryCount(dbQuery, opt = {}) {
151
- const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery.select([]), this.ds().createQuery(dbQuery.table));
152
- const aq = this.ds().createAggregationQuery(q).count('count');
162
+ const ds = await this.ds();
163
+ const q = dbQueryToDatastoreQuery(dbQuery.select([]), ds.createQuery(dbQuery.table), await this.getPropertyFilter());
164
+ const aq = ds.createAggregationQuery(q).count('count');
153
165
  const dsOpt = this.getRunQueryOptions(opt);
154
- const [entities] = await this.ds().runAggregationQuery(aq, dsOpt);
166
+ const [entities] = await ds.runAggregationQuery(aq, dsOpt);
155
167
  return entities[0]?.count;
156
168
  }
157
169
  async runDatastoreQuery(q, dsOpt) {
158
- const [entities, queryResult] = await this.ds().runQuery(q, dsOpt);
170
+ const ds = await this.ds();
171
+ const [entities, queryResult] = await ds.runQuery(q, dsOpt);
159
172
  const rows = entities.map(e => this.mapId(e));
160
173
  return {
161
174
  ...queryResult,
162
175
  rows,
163
176
  };
164
177
  }
165
- runQueryStream(q, _opt) {
166
- const opt = {
167
- ...this.cfg.streamOptions,
168
- ..._opt,
169
- };
170
- const dsOpt = this.getRunQueryOptions(opt);
171
- return (opt.experimentalCursorStream
172
- ? new DatastoreStreamReadable_1.DatastoreStreamReadable(q, opt, (0, js_lib_1.commonLoggerMinLevel)(this.cfg.logger, opt.debug ? 'log' : 'warn'))
173
- : this.ds().runQueryStream(q, dsOpt)).map(chunk => this.mapId(chunk));
174
- }
175
- streamQuery(dbQuery, opt) {
176
- const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery, this.ds().createQuery(dbQuery.table));
177
- return this.runQueryStream(q, opt);
178
+ streamQuery(dbQuery, _opt) {
179
+ const transform = new Transform({
180
+ objectMode: true,
181
+ transform: (chunk, _, cb) => {
182
+ cb(null, this.mapId(chunk));
183
+ },
184
+ });
185
+ void this.ds().then(async (ds) => {
186
+ const q = dbQueryToDatastoreQuery(dbQuery, ds.createQuery(dbQuery.table), await this.getPropertyFilter());
187
+ const opt = {
188
+ ...this.cfg.streamOptions,
189
+ ..._opt,
190
+ };
191
+ (opt.experimentalCursorStream
192
+ ? new DatastoreStreamReadable(q, opt, commonLoggerMinLevel(this.cfg.logger, opt.debug ? 'log' : 'warn'))
193
+ : ds.runQueryStream(q, this.getRunQueryOptions(opt)))
194
+ .on('error', err => transform.emit('error', err))
195
+ .pipe(transform);
196
+ });
197
+ return transform;
178
198
  }
179
199
  // https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/2-structured-data/books/model-datastore.js
180
200
  /**
181
201
  * Returns saved entities with generated id/updated/created (non-mutating!)
182
202
  */
183
203
  async saveBatch(table, rows, opt = {}) {
184
- const entities = rows.map(obj => this.toDatastoreEntity(table, obj, opt.excludeFromIndexes));
204
+ const ds = await this.ds();
205
+ const entities = rows.map(obj => this.toDatastoreEntity(ds, table, obj, opt.excludeFromIndexes));
185
206
  const method = methodMap[opt.saveMethod || 'upsert'] || 'save';
186
- const save = (0, js_lib_1.pRetryFn)(async (batch) => {
187
- await (opt.tx?.tx || this.ds())[method](batch);
207
+ const save = pRetryFn(async (batch) => {
208
+ await (opt.tx?.tx || ds)[method](batch);
188
209
  }, this.getPRetryOptions(`DatastoreLib.saveBatch(${table})`));
189
210
  try {
190
- const chunks = (0, js_lib_1._chunk)(entities, MAX_ITEMS);
211
+ const chunks = _chunk(entities, MAX_ITEMS);
191
212
  if (chunks.length === 1) {
192
213
  // Not using pMap in hope to preserve stack trace
193
214
  await save(chunks[0]);
194
215
  }
195
216
  else {
196
- await (0, js_lib_1.pMap)(chunks, async (batch) => await save(batch), {
217
+ await pMap(chunks, async (batch) => await save(batch), {
197
218
  concurrency: DATASTORE_RECOMMENDED_CONCURRENCY,
198
219
  });
199
220
  }
200
221
  }
201
222
  catch (err) {
202
- if (err instanceof js_lib_1.TimeoutError) {
203
- (0, js_lib_1._errorDataAppend)(err, {
223
+ if (err instanceof TimeoutError) {
224
+ _errorDataAppend(err, {
204
225
  fingerprint: [DATASTORE_TIMEOUT],
205
226
  });
206
227
  }
@@ -215,7 +236,8 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
215
236
  const ids = idFilter.op === '==' ? [idFilter.val] : idFilter.val;
216
237
  return await this.deleteByIds(q.table, ids, opt);
217
238
  }
218
- const datastoreQuery = (0, query_util_1.dbQueryToDatastoreQuery)(q.select([]), this.ds().createQuery(q.table));
239
+ const ds = await this.ds();
240
+ const datastoreQuery = dbQueryToDatastoreQuery(q.select([]), ds.createQuery(q.table), await this.getPropertyFilter());
219
241
  const dsOpt = this.getRunQueryOptions(opt);
220
242
  const { rows } = await this.runDatastoreQuery(datastoreQuery, dsOpt);
221
243
  return await this.deleteByIds(q.table, rows.map(obj => obj.id), opt);
@@ -225,15 +247,17 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
225
247
  * regardless if they were actually deleted or not.
226
248
  */
227
249
  async deleteByIds(table, ids, opt = {}) {
228
- const keys = ids.map(id => this.key(table, id));
229
- await (0, js_lib_1.pMap)((0, js_lib_1._chunk)(keys, MAX_ITEMS), async (batch) => await (opt.tx?.tx || this.ds()).delete(batch), {
250
+ const ds = await this.ds();
251
+ const keys = ids.map(id => this.key(ds, table, id));
252
+ await pMap(_chunk(keys, MAX_ITEMS), async (batch) => await (opt.tx?.tx || ds).delete(batch), {
230
253
  concurrency: DATASTORE_RECOMMENDED_CONCURRENCY,
231
254
  });
232
255
  return ids.length;
233
256
  }
234
257
  async runInTransaction(fn, opt = {}) {
258
+ const ds = await this.ds();
235
259
  const { readOnly } = opt;
236
- const datastoreTx = this.ds().transaction({
260
+ const datastoreTx = ds.transaction({
237
261
  readOnly,
238
262
  });
239
263
  try {
@@ -248,20 +272,23 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
248
272
  }
249
273
  }
250
274
  async getAllStats() {
251
- const q = this.ds().createQuery('__Stat_Kind__');
252
- const [statsArray] = await this.ds().runQuery(q);
275
+ const ds = await this.ds();
276
+ const q = ds.createQuery('__Stat_Kind__');
277
+ const [statsArray] = await ds.runQuery(q);
253
278
  return statsArray || [];
254
279
  }
255
280
  /**
256
281
  * Returns undefined e.g when Table is non-existing
257
282
  */
258
283
  async getStats(table) {
259
- const q = this.ds()
284
+ const ds = await this.ds();
285
+ const propertyFilter = await this.getPropertyFilter();
286
+ const q = ds
260
287
  .createQuery('__Stat_Kind__')
261
288
  // .filter('kind_name', table)
262
- .filter(new datastore_1.PropertyFilter('kind_name', '=', table))
289
+ .filter(new propertyFilter('kind_name', '=', table))
263
290
  .limit(1);
264
- const [statsArray] = await this.ds().runQuery(q);
291
+ const [statsArray] = await ds.runQuery(q);
265
292
  const [stats] = statsArray;
266
293
  return stats;
267
294
  }
@@ -270,11 +297,12 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
270
297
  return stats?.count;
271
298
  }
272
299
  async getTableProperties(table) {
273
- const q = this.ds()
300
+ const ds = await this.ds();
301
+ const q = ds
274
302
  .createQuery('__Stat_PropertyType_PropertyName_Kind__')
275
303
  // .filter('kind_name', table)
276
- .filter(new datastore_1.PropertyFilter('kind_name', '=', table));
277
- const [stats] = await this.ds().runQuery(q);
304
+ .filter(new (await this.getPropertyFilter())('kind_name', '=', table));
305
+ const [stats] = await ds.runQuery(q);
278
306
  return stats;
279
307
  }
280
308
  mapId(o, preserveKey = false) {
@@ -289,8 +317,8 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
289
317
  return r;
290
318
  }
291
319
  // if key field exists on entity, it will be used as key (prevent to duplication of numeric keyed entities)
292
- toDatastoreEntity(kind, o, excludeFromIndexes = []) {
293
- const key = this.getDsKey(o) || this.key(kind, o.id);
320
+ toDatastoreEntity(ds, kind, o, excludeFromIndexes = []) {
321
+ const key = this.getDsKey(o) || this.key(ds, kind, o.id);
294
322
  const data = Object.assign({}, o);
295
323
  delete data.id;
296
324
  delete data[this.KEY];
@@ -300,9 +328,9 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
300
328
  excludeFromIndexes,
301
329
  };
302
330
  }
303
- key(kind, id) {
304
- (0, js_lib_1._assert)(id, `Cannot save "${kind}" entity without "id"`);
305
- return this.ds().key([kind, id]);
331
+ key(ds, kind, id) {
332
+ _assert(id, `Cannot save "${kind}" entity without "id"`);
333
+ return ds.key([kind, id]);
306
334
  }
307
335
  getDsKey(o) {
308
336
  return o?.[this.KEY];
@@ -333,17 +361,17 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
333
361
  .forEach(stats => {
334
362
  const { property_type: dtype } = stats;
335
363
  const name = stats.property_name;
336
- if (dtype === datastore_model_1.DatastoreType.Blob) {
364
+ if (dtype === DatastoreType.Blob) {
337
365
  s.properties[name] = {
338
366
  instanceof: 'Buffer',
339
367
  };
340
368
  }
341
- else if (dtype === datastore_model_1.DatastoreType.Text || dtype === datastore_model_1.DatastoreType.String) {
369
+ else if (dtype === DatastoreType.Text || dtype === DatastoreType.String) {
342
370
  s.properties[name] = {
343
371
  type: 'string',
344
372
  };
345
373
  }
346
- else if (dtype === datastore_model_1.DatastoreType.EmbeddedEntity) {
374
+ else if (dtype === DatastoreType.EmbeddedEntity) {
347
375
  s.properties[name] = {
348
376
  type: 'object',
349
377
  additionalProperties: true,
@@ -351,26 +379,26 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
351
379
  required: [],
352
380
  };
353
381
  }
354
- else if (dtype === datastore_model_1.DatastoreType.Integer) {
382
+ else if (dtype === DatastoreType.Integer) {
355
383
  s.properties[name] = {
356
384
  type: 'integer',
357
385
  };
358
386
  }
359
- else if (dtype === datastore_model_1.DatastoreType.Float) {
387
+ else if (dtype === DatastoreType.Float) {
360
388
  s.properties[name] = {
361
389
  type: 'number',
362
390
  };
363
391
  }
364
- else if (dtype === datastore_model_1.DatastoreType.Boolean) {
392
+ else if (dtype === DatastoreType.Boolean) {
365
393
  s.properties[name] = {
366
394
  type: 'boolean',
367
395
  };
368
396
  }
369
- else if (dtype === datastore_model_1.DatastoreType.DATE_TIME) {
397
+ else if (dtype === DatastoreType.DATE_TIME) {
370
398
  // Don't know how to map it properly
371
399
  s.properties[name] = {};
372
400
  }
373
- else if (dtype === datastore_model_1.DatastoreType.NULL) {
401
+ else if (dtype === DatastoreType.NULL) {
374
402
  // check, maybe we can just skip this type and do nothing?
375
403
  s.properties[name] ||= {
376
404
  type: 'null',
@@ -422,11 +450,12 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
422
450
  };
423
451
  }
424
452
  }
425
- exports.DatastoreDB = DatastoreDB;
426
453
  /**
427
454
  * https://cloud.google.com/datastore/docs/concepts/transactions#datastore-datastore-transactional-update-nodejs
428
455
  */
429
- class DatastoreDBTransaction {
456
+ export class DatastoreDBTransaction {
457
+ db;
458
+ tx;
430
459
  constructor(db, tx) {
431
460
  this.db = db;
432
461
  this.tx = tx;
@@ -450,4 +479,3 @@ class DatastoreDBTransaction {
450
479
  return await this.db.deleteByIds(table, ids, { ...opt, tx: this });
451
480
  }
452
481
  }
453
- exports.DatastoreDBTransaction = DatastoreDBTransaction;
@@ -1,6 +1,6 @@
1
1
  import type { DatastoreOptions, Key } from '@google-cloud/datastore';
2
- import { CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib';
3
- import { CommonLogger, NumberOfSeconds, ObjectWithId } from '@naturalcycles/js-lib';
2
+ import type { CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib';
3
+ import type { CommonLogger, NumberOfSeconds, ObjectWithId } from '@naturalcycles/js-lib';
4
4
  export interface DatastorePayload<T = any> {
5
5
  key: Key;
6
6
  data: T;
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatastoreType = void 0;
4
- var DatastoreType;
1
+ export var DatastoreType;
5
2
  (function (DatastoreType) {
6
3
  DatastoreType["Blob"] = "Blob";
7
4
  DatastoreType["Text"] = "Text";
@@ -12,4 +9,4 @@ var DatastoreType;
12
9
  DatastoreType["DATE_TIME"] = "Date/Time";
13
10
  DatastoreType["Boolean"] = "Boolean";
14
11
  DatastoreType["NULL"] = "NULL";
15
- })(DatastoreType || (exports.DatastoreType = DatastoreType = {}));
12
+ })(DatastoreType || (DatastoreType = {}));
@@ -1,8 +1,7 @@
1
- import { CommonKeyValueDB, KeyValueDBTuple } from '@naturalcycles/db-lib';
2
- import { IncrementTuple } from '@naturalcycles/db-lib/dist/kv/commonKeyValueDB';
3
- import { ReadableTyped } from '@naturalcycles/nodejs-lib';
4
- import { DatastoreDB } from './datastore.db';
5
- import { DatastoreDBCfg } from './datastore.model';
1
+ import type { CommonKeyValueDB, IncrementTuple, KeyValueDBTuple } from '@naturalcycles/db-lib';
2
+ import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
+ import { DatastoreDB } from './datastore.db.js';
4
+ import type { DatastoreDBCfg } from './datastore.model.js';
6
5
  export interface DatastoreKeyValueDBCfg extends DatastoreDBCfg {
7
6
  }
8
7
  export declare class DatastoreKeyValueDB implements CommonKeyValueDB {
@@ -1,19 +1,18 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatastoreKeyValueDB = void 0;
4
- const db_lib_1 = require("@naturalcycles/db-lib");
5
- const js_lib_1 = require("@naturalcycles/js-lib");
6
- const datastore_db_1 = require("./datastore.db");
1
+ import { commonKeyValueDBFullSupport, DBQuery } from '@naturalcycles/db-lib';
2
+ import { AppError } from '@naturalcycles/js-lib';
3
+ import { DatastoreDB } from './datastore.db.js';
7
4
  const excludeFromIndexes = ['v'];
8
- class DatastoreKeyValueDB {
5
+ export class DatastoreKeyValueDB {
6
+ cfg;
9
7
  constructor(cfg) {
10
8
  this.cfg = cfg;
11
- this.support = {
12
- ...db_lib_1.commonKeyValueDBFullSupport,
13
- increment: false,
14
- };
15
- this.db = new datastore_db_1.DatastoreDB(cfg);
9
+ this.db = new DatastoreDB(cfg);
16
10
  }
11
+ db;
12
+ support = {
13
+ ...commonKeyValueDBFullSupport,
14
+ increment: false,
15
+ };
17
16
  async ping() {
18
17
  await this.db.ping();
19
18
  }
@@ -30,7 +29,7 @@ class DatastoreKeyValueDB {
30
29
  });
31
30
  }
32
31
  streamIds(table, limit) {
33
- const q = db_lib_1.DBQuery.create(table)
32
+ const q = DBQuery.create(table)
34
33
  .select(['id'])
35
34
  .limit(limit || 0);
36
35
  return (this.db
@@ -40,25 +39,24 @@ class DatastoreKeyValueDB {
40
39
  }
41
40
  streamValues(table, limit) {
42
41
  // `select v` doesn't work for some reason
43
- const q = db_lib_1.DBQuery.create(table).limit(limit || 0);
42
+ const q = DBQuery.create(table).limit(limit || 0);
44
43
  return (this.db
45
44
  .streamQuery(q)
46
45
  // .on('error', err => stream.emit('error', err))
47
46
  .map(r => r.v));
48
47
  }
49
48
  streamEntries(table, limit) {
50
- const q = db_lib_1.DBQuery.create(table).limit(limit || 0);
49
+ const q = DBQuery.create(table).limit(limit || 0);
51
50
  return (this.db
52
51
  .streamQuery(q)
53
52
  // .on('error', err => stream.emit('error', err))
54
53
  .map(r => [r.id, r.v]));
55
54
  }
56
55
  async count(table) {
57
- const q = db_lib_1.DBQuery.create(table);
56
+ const q = DBQuery.create(table);
58
57
  return await this.db.runQueryCount(q);
59
58
  }
60
59
  async incrementBatch(_table, _entries) {
61
- throw new js_lib_1.AppError('DatastoreKeyValueDB.incrementBatch() is not implemented');
60
+ throw new AppError('DatastoreKeyValueDB.incrementBatch() is not implemented');
62
61
  }
63
62
  }
64
- exports.DatastoreKeyValueDB = DatastoreKeyValueDB;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from './datastore.db';
2
- export * from './datastore.model';
3
- export * from './datastoreKeyValueDB';
1
+ export * from './datastore.db.js';
2
+ export * from './datastore.model.js';
3
+ export * from './datastoreKeyValueDB.js';
package/dist/index.js CHANGED
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- tslib_1.__exportStar(require("./datastore.db"), exports);
5
- tslib_1.__exportStar(require("./datastore.model"), exports);
6
- tslib_1.__exportStar(require("./datastoreKeyValueDB"), exports);
1
+ export * from './datastore.db.js';
2
+ export * from './datastore.model.js';
3
+ export * from './datastoreKeyValueDB.js';
@@ -1,4 +1,4 @@
1
- import { Query } from '@google-cloud/datastore';
2
- import { DBQuery } from '@naturalcycles/db-lib';
3
- import { ObjectWithId } from '@naturalcycles/js-lib';
4
- export declare function dbQueryToDatastoreQuery<ROW extends ObjectWithId>(dbQuery: Readonly<DBQuery<ROW>>, emptyQuery: Query): Query;
1
+ import type { PropertyFilter, Query } from '@google-cloud/datastore';
2
+ import type { DBQuery } from '@naturalcycles/db-lib';
3
+ import type { ObjectWithId } from '@naturalcycles/js-lib';
4
+ export declare function dbQueryToDatastoreQuery<ROW extends ObjectWithId>(dbQuery: Readonly<DBQuery<ROW>>, emptyQuery: Query, propertyFilterClass: typeof PropertyFilter): Query;
@@ -1,7 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dbQueryToDatastoreQuery = dbQueryToDatastoreQuery;
4
- const datastore_1 = require("@google-cloud/datastore");
5
1
  const FNAME_MAP = {
6
2
  id: '__key__',
7
3
  };
@@ -10,7 +6,7 @@ const OP_MAP = {
10
6
  in: 'IN',
11
7
  'not-in': 'NOT_IN',
12
8
  };
13
- function dbQueryToDatastoreQuery(dbQuery, emptyQuery) {
9
+ export function dbQueryToDatastoreQuery(dbQuery, emptyQuery, propertyFilterClass) {
14
10
  let q = emptyQuery;
15
11
  // filter
16
12
  for (const f of dbQuery._filters) {
@@ -24,7 +20,7 @@ function dbQueryToDatastoreQuery(dbQuery, emptyQuery) {
24
20
  let { op, val } = f;
25
21
  if (val === undefined)
26
22
  val = null;
27
- q = q.filter(new datastore_1.PropertyFilter(f.name, OP_MAP[op] || op, val));
23
+ q = q.filter(new propertyFilterClass(f.name, OP_MAP[op] || op, val));
28
24
  }
29
25
  // limit
30
26
  q = q.limit(dbQuery._limitValue || 0);
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/datastore-lib",
3
- "version": "3.39.3",
3
+ "type": "module",
4
+ "version": "4.0.0",
4
5
  "description": "Opinionated library to work with Google Datastore",
5
6
  "scripts": {
6
7
  "prepare": "husky",
@@ -12,14 +13,16 @@
12
13
  },
13
14
  "dependencies": {
14
15
  "@google-cloud/datastore": "^9",
15
- "@naturalcycles/db-lib": "^9",
16
+ "@naturalcycles/db-lib": "^10",
16
17
  "@naturalcycles/js-lib": "^14",
17
18
  "@naturalcycles/nodejs-lib": "^13"
18
19
  },
19
20
  "devDependencies": {
20
- "@naturalcycles/dev-lib": "^15",
21
+ "@naturalcycles/dev-lib": "^17",
21
22
  "@types/node": "^22",
22
- "jest": "^29"
23
+ "@vitest/coverage-v8": "^3",
24
+ "tsx": "^4.19.3",
25
+ "vitest": "^3"
23
26
  },
24
27
  "files": [
25
28
  "dist",
@@ -1,9 +1,10 @@
1
1
  import { Readable } from 'node:stream'
2
- import { Query } from '@google-cloud/datastore'
3
- import type { RunQueryInfo, RunQueryOptions } from '@google-cloud/datastore/build/src/query'
4
- import { _ms, CommonLogger, pRetry, UnixTimestampMillis } from '@naturalcycles/js-lib'
2
+ import type { Query } from '@google-cloud/datastore'
3
+ import type { RunQueryInfo, RunQueryOptions } from '@google-cloud/datastore/build/src/query.js'
4
+ import type { CommonLogger, UnixTimestampMillis } from '@naturalcycles/js-lib'
5
+ import { _ms, pRetry } from '@naturalcycles/js-lib'
5
6
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
6
- import type { DatastoreDBStreamOptions } from './datastore.model'
7
+ import type { DatastoreDBStreamOptions } from './datastore.model.js'
7
8
 
8
9
  export class DatastoreStreamReadable<T = any> extends Readable implements ReadableTyped<T> {
9
10
  private readonly originalLimit: number
@@ -1,10 +1,8 @@
1
- import type { Datastore, Key, Query } from '@google-cloud/datastore'
2
- import { PropertyFilter, Transaction } from '@google-cloud/datastore'
3
- import { type RunQueryOptions } from '@google-cloud/datastore/build/src/query'
4
- import {
5
- BaseCommonDB,
1
+ import { Transform } from 'node:stream'
2
+ import type { Datastore, Key, PropertyFilter, Query, Transaction } from '@google-cloud/datastore'
3
+ import type { RunQueryOptions } from '@google-cloud/datastore/build/src/query.js'
4
+ import type {
6
5
  CommonDB,
7
- commonDBFullSupport,
8
6
  CommonDBOptions,
9
7
  CommonDBReadOptions,
10
8
  CommonDBSaveMethod,
@@ -16,13 +14,9 @@ import {
16
14
  DBTransactionFn,
17
15
  RunQueryResult,
18
16
  } from '@naturalcycles/db-lib'
19
- import {
20
- _assert,
21
- _chunk,
22
- _errorDataAppend,
23
- _omit,
17
+ import { BaseCommonDB, commonDBFullSupport } from '@naturalcycles/db-lib'
18
+ import type {
24
19
  CommonLogger,
25
- commonLoggerMinLevel,
26
20
  JsonSchemaAny,
27
21
  JsonSchemaBoolean,
28
22
  JsonSchemaNull,
@@ -31,15 +25,23 @@ import {
31
25
  JsonSchemaRootObject,
32
26
  JsonSchemaString,
33
27
  ObjectWithId,
28
+ PRetryOptions,
29
+ } from '@naturalcycles/js-lib'
30
+ import {
31
+ _assert,
32
+ _chunk,
33
+ _errorDataAppend,
34
+ _omit,
35
+ commonLoggerMinLevel,
34
36
  pMap,
35
37
  pRetry,
36
38
  pRetryFn,
37
- PRetryOptions,
38
39
  pTimeout,
39
40
  TimeoutError,
40
41
  } from '@naturalcycles/js-lib'
41
- import { boldWhite, ReadableTyped } from '@naturalcycles/nodejs-lib'
42
- import {
42
+ import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
43
+ import { boldWhite } from '@naturalcycles/nodejs-lib'
44
+ import type {
43
45
  DatastoreDBCfg,
44
46
  DatastoreDBOptions,
45
47
  DatastoreDBReadOptions,
@@ -48,10 +50,10 @@ import {
48
50
  DatastorePayload,
49
51
  DatastorePropertyStats,
50
52
  DatastoreStats,
51
- DatastoreType,
52
- } from './datastore.model'
53
- import { DatastoreStreamReadable } from './DatastoreStreamReadable'
54
- import { dbQueryToDatastoreQuery } from './query.util'
53
+ } from './datastore.model.js'
54
+ import { DatastoreType } from './datastore.model.js'
55
+ import { DatastoreStreamReadable } from './DatastoreStreamReadable.js'
56
+ import { dbQueryToDatastoreQuery } from './query.util.js'
55
57
 
56
58
  // Datastore (also Firestore and other Google APIs) supports max 500 of items when saving/deleting, etc.
57
59
  const MAX_ITEMS = 500
@@ -107,16 +109,14 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
107
109
  protected KEY!: symbol
108
110
 
109
111
  // @memo() // not used to be able to connect to many DBs in the same server instance
110
- ds(): Datastore {
112
+ async ds(): Promise<Datastore> {
111
113
  if (!this.cachedDatastore) {
112
114
  _assert(
113
115
  process.env['APP_ENV'] !== 'test',
114
116
  'DatastoreDB cannot be used in Test env, please use InMemoryDB',
115
117
  )
116
118
 
117
- // Lazy-loading
118
- const datastoreLib = require('@google-cloud/datastore')
119
- const DS = datastoreLib.Datastore as typeof Datastore
119
+ const DS = (await this.getDatastoreLib()).Datastore as typeof Datastore
120
120
  this.cfg.projectId ||= this.cfg.credentials?.project_id || process.env['GOOGLE_CLOUD_PROJECT']
121
121
 
122
122
  if (this.cfg.projectId) {
@@ -136,6 +136,16 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
136
136
  return this.cachedDatastore
137
137
  }
138
138
 
139
+ private async getPropertyFilter(): Promise<typeof PropertyFilter> {
140
+ return (await this.getDatastoreLib()).PropertyFilter
141
+ }
142
+
143
+ private async getDatastoreLib(): Promise<any> {
144
+ // Lazy-loading
145
+ const { default: lib } = await import('@google-cloud/datastore')
146
+ return lib
147
+ }
148
+
139
149
  override async ping(): Promise<void> {
140
150
  await this.getAllStats()
141
151
  }
@@ -146,7 +156,8 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
146
156
  opt: DatastoreDBReadOptions = {},
147
157
  ): Promise<ROW[]> {
148
158
  if (!ids.length) return []
149
- const keys = ids.map(id => this.key(table, id))
159
+ let ds = await this.ds()
160
+ const keys = ids.map(id => this.key(ds, table, id))
150
161
  let rows: any[]
151
162
 
152
163
  const dsOpt = this.getRunQueryOptions(opt)
@@ -155,7 +166,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
155
166
  // First try
156
167
  try {
157
168
  const r = await pTimeout(
158
- () => ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).get(keys, dsOpt),
169
+ () => ((opt.tx as DatastoreDBTransaction)?.tx || ds).get(keys, dsOpt),
159
170
  {
160
171
  timeout: this.cfg.timeout,
161
172
  name: `datastore.getByIds(${table})`,
@@ -171,14 +182,14 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
171
182
  this.cfg.logger.log('datastore recreated on error')
172
183
 
173
184
  // This is to debug "GCP Datastore Timeout issue"
174
- const datastoreLib = require('@google-cloud/datastore')
185
+ const datastoreLib = await this.getDatastoreLib()
175
186
  const DS = datastoreLib.Datastore as typeof Datastore
176
- this.cachedDatastore = new DS(this.cfg)
187
+ ds = this.cachedDatastore = new DS(this.cfg)
177
188
 
178
189
  // Second try (will throw)
179
190
  try {
180
191
  const r = await pRetry(
181
- () => ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).get(keys, dsOpt),
192
+ () => ((opt.tx as DatastoreDBTransaction)?.tx || ds).get(keys, dsOpt),
182
193
  {
183
194
  ...this.getPRetryOptions(`datastore.getByIds(${table}) second try`),
184
195
  maxAttempts: 3,
@@ -198,7 +209,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
198
209
  } else {
199
210
  rows = await pRetry(
200
211
  async () => {
201
- return (await this.ds().get(keys, dsOpt))[0]
212
+ return (await ds.get(keys, dsOpt))[0]
202
213
  },
203
214
  this.getPRetryOptions(`datastore.getByIds(${table})`),
204
215
  )
@@ -231,7 +242,12 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
231
242
  }
232
243
  }
233
244
 
234
- const q = dbQueryToDatastoreQuery(dbQuery, this.ds().createQuery(dbQuery.table))
245
+ const ds = await this.ds()
246
+ const q = dbQueryToDatastoreQuery(
247
+ dbQuery,
248
+ ds.createQuery(dbQuery.table),
249
+ await this.getPropertyFilter(),
250
+ )
235
251
  const dsOpt = this.getRunQueryOptions(opt)
236
252
  const qr = await this.runDatastoreQuery<ROW>(q, dsOpt)
237
253
 
@@ -247,10 +263,15 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
247
263
  dbQuery: DBQuery<ROW>,
248
264
  opt: DatastoreDBReadOptions = {},
249
265
  ): Promise<number> {
250
- const q = dbQueryToDatastoreQuery(dbQuery.select([]), this.ds().createQuery(dbQuery.table))
251
- const aq = this.ds().createAggregationQuery(q).count('count')
266
+ const ds = await this.ds()
267
+ const q = dbQueryToDatastoreQuery(
268
+ dbQuery.select([]),
269
+ ds.createQuery(dbQuery.table),
270
+ await this.getPropertyFilter(),
271
+ )
272
+ const aq = ds.createAggregationQuery(q).count('count')
252
273
  const dsOpt = this.getRunQueryOptions(opt)
253
- const [entities] = await this.ds().runAggregationQuery(aq, dsOpt)
274
+ const [entities] = await ds.runAggregationQuery(aq, dsOpt)
254
275
  return entities[0]?.count
255
276
  }
256
277
 
@@ -258,7 +279,8 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
258
279
  q: Query,
259
280
  dsOpt: RunQueryOptions,
260
281
  ): Promise<RunQueryResult<ROW>> {
261
- const [entities, queryResult] = await this.ds().runQuery(q, dsOpt)
282
+ const ds = await this.ds()
283
+ const [entities, queryResult] = await ds.runQuery(q, dsOpt)
262
284
 
263
285
  const rows = entities.map(e => this.mapId<ROW>(e))
264
286
 
@@ -268,33 +290,42 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
268
290
  }
269
291
  }
270
292
 
271
- private runQueryStream<ROW extends ObjectWithId>(
272
- q: Query,
293
+ override streamQuery<ROW extends ObjectWithId>(
294
+ dbQuery: DBQuery<ROW>,
273
295
  _opt?: DatastoreDBStreamOptions,
274
296
  ): ReadableTyped<ROW> {
275
- const opt = {
276
- ...this.cfg.streamOptions,
277
- ..._opt,
278
- }
279
- const dsOpt = this.getRunQueryOptions(opt)
297
+ const transform = new Transform({
298
+ objectMode: true,
299
+ transform: (chunk, _, cb) => {
300
+ cb(null, this.mapId(chunk))
301
+ },
302
+ })
280
303
 
281
- return (
282
- opt.experimentalCursorStream
304
+ void this.ds().then(async ds => {
305
+ const q = dbQueryToDatastoreQuery(
306
+ dbQuery,
307
+ ds.createQuery(dbQuery.table),
308
+ await this.getPropertyFilter(),
309
+ )
310
+
311
+ const opt = {
312
+ ...this.cfg.streamOptions,
313
+ ..._opt,
314
+ }
315
+
316
+ ;(opt.experimentalCursorStream
283
317
  ? new DatastoreStreamReadable<ROW>(
284
318
  q,
285
319
  opt,
286
320
  commonLoggerMinLevel(this.cfg.logger, opt.debug ? 'log' : 'warn'),
287
321
  )
288
- : (this.ds().runQueryStream(q, dsOpt) as ReadableTyped<ROW>)
289
- ).map(chunk => this.mapId(chunk))
290
- }
322
+ : (ds.runQueryStream(q, this.getRunQueryOptions(opt)) as ReadableTyped<ROW>)
323
+ )
324
+ .on('error', err => transform.emit('error', err))
325
+ .pipe(transform)
326
+ })
291
327
 
292
- override streamQuery<ROW extends ObjectWithId>(
293
- dbQuery: DBQuery<ROW>,
294
- opt?: DatastoreDBStreamOptions,
295
- ): ReadableTyped<ROW> {
296
- const q = dbQueryToDatastoreQuery(dbQuery, this.ds().createQuery(dbQuery.table))
297
- return this.runQueryStream(q, opt)
328
+ return transform
298
329
  }
299
330
 
300
331
  // https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/2-structured-data/books/model-datastore.js
@@ -307,15 +338,16 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
307
338
  rows: ROW[],
308
339
  opt: DatastoreDBSaveOptions<ROW> = {},
309
340
  ): Promise<void> {
341
+ const ds = await this.ds()
310
342
  const entities = rows.map(obj =>
311
- this.toDatastoreEntity(table, obj, opt.excludeFromIndexes as string[]),
343
+ this.toDatastoreEntity(ds, table, obj, opt.excludeFromIndexes as string[]),
312
344
  )
313
345
 
314
346
  const method = methodMap[opt.saveMethod || 'upsert'] || 'save'
315
347
 
316
348
  const save = pRetryFn(
317
349
  async (batch: DatastorePayload<ROW>[]) => {
318
- await ((opt.tx as DatastoreDBTransaction)?.tx || this.ds())[method](batch)
350
+ await ((opt.tx as DatastoreDBTransaction)?.tx || ds)[method](batch)
319
351
  },
320
352
  this.getPRetryOptions(`DatastoreLib.saveBatch(${table})`),
321
353
  )
@@ -357,7 +389,12 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
357
389
  return await this.deleteByIds(q.table, ids, opt)
358
390
  }
359
391
 
360
- const datastoreQuery = dbQueryToDatastoreQuery(q.select([]), this.ds().createQuery(q.table))
392
+ const ds = await this.ds()
393
+ const datastoreQuery = dbQueryToDatastoreQuery(
394
+ q.select([]),
395
+ ds.createQuery(q.table),
396
+ await this.getPropertyFilter(),
397
+ )
361
398
  const dsOpt = this.getRunQueryOptions(opt)
362
399
  const { rows } = await this.runDatastoreQuery<ROW>(datastoreQuery, dsOpt)
363
400
  return await this.deleteByIds(
@@ -376,12 +413,13 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
376
413
  ids: string[],
377
414
  opt: DatastoreDBOptions = {},
378
415
  ): Promise<number> {
379
- const keys = ids.map(id => this.key(table, id))
416
+ const ds = await this.ds()
417
+ const keys = ids.map(id => this.key(ds, table, id))
380
418
 
381
419
  await pMap(
382
420
  _chunk(keys, MAX_ITEMS),
383
421
 
384
- async batch => await ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).delete(batch),
422
+ async batch => await ((opt.tx as DatastoreDBTransaction)?.tx || ds).delete(batch),
385
423
  {
386
424
  concurrency: DATASTORE_RECOMMENDED_CONCURRENCY,
387
425
  },
@@ -393,8 +431,9 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
393
431
  fn: DBTransactionFn,
394
432
  opt: CommonDBTransactionOptions = {},
395
433
  ): Promise<void> {
434
+ const ds = await this.ds()
396
435
  const { readOnly } = opt
397
- const datastoreTx = this.ds().transaction({
436
+ const datastoreTx = ds.transaction({
398
437
  readOnly,
399
438
  })
400
439
 
@@ -410,8 +449,9 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
410
449
  }
411
450
 
412
451
  async getAllStats(): Promise<DatastoreStats[]> {
413
- const q = this.ds().createQuery('__Stat_Kind__')
414
- const [statsArray] = await this.ds().runQuery(q)
452
+ const ds = await this.ds()
453
+ const q = ds.createQuery('__Stat_Kind__')
454
+ const [statsArray] = await ds.runQuery(q)
415
455
  return statsArray || []
416
456
  }
417
457
 
@@ -419,12 +459,15 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
419
459
  * Returns undefined e.g when Table is non-existing
420
460
  */
421
461
  async getStats(table: string): Promise<DatastoreStats | undefined> {
422
- const q = this.ds()
462
+ const ds = await this.ds()
463
+ const propertyFilter = await this.getPropertyFilter()
464
+
465
+ const q = ds
423
466
  .createQuery('__Stat_Kind__')
424
467
  // .filter('kind_name', table)
425
- .filter(new PropertyFilter('kind_name', '=', table))
468
+ .filter(new propertyFilter('kind_name', '=', table))
426
469
  .limit(1)
427
- const [statsArray] = await this.ds().runQuery(q)
470
+ const [statsArray] = await ds.runQuery(q)
428
471
  const [stats] = statsArray
429
472
  return stats
430
473
  }
@@ -435,11 +478,12 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
435
478
  }
436
479
 
437
480
  async getTableProperties(table: string): Promise<DatastorePropertyStats[]> {
438
- const q = this.ds()
481
+ const ds = await this.ds()
482
+ const q = ds
439
483
  .createQuery('__Stat_PropertyType_PropertyName_Kind__')
440
484
  // .filter('kind_name', table)
441
- .filter(new PropertyFilter('kind_name', '=', table))
442
- const [stats] = await this.ds().runQuery(q)
485
+ .filter(new (await this.getPropertyFilter())('kind_name', '=', table))
486
+ const [stats] = await ds.runQuery(q)
443
487
  return stats
444
488
  }
445
489
 
@@ -455,11 +499,12 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
455
499
 
456
500
  // if key field exists on entity, it will be used as key (prevent to duplication of numeric keyed entities)
457
501
  private toDatastoreEntity<T extends ObjectWithId>(
502
+ ds: Datastore,
458
503
  kind: string,
459
504
  o: T,
460
505
  excludeFromIndexes: string[] = [],
461
506
  ): DatastorePayload<T> {
462
- const key = this.getDsKey(o) || this.key(kind, o.id)
507
+ const key = this.getDsKey(o) || this.key(ds, kind, o.id)
463
508
  const data = Object.assign({}, o) as any
464
509
  delete data.id
465
510
  delete data[this.KEY]
@@ -471,9 +516,9 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
471
516
  }
472
517
  }
473
518
 
474
- key(kind: string, id: string): Key {
519
+ key(ds: Datastore, kind: string, id: string): Key {
475
520
  _assert(id, `Cannot save "${kind}" entity without "id"`)
476
- return this.ds().key([kind, id])
521
+ return ds.key([kind, id])
477
522
  }
478
523
 
479
524
  private getDsKey(o: any): Key | undefined {
@@ -1,6 +1,10 @@
1
1
  import type { DatastoreOptions, Key } from '@google-cloud/datastore'
2
- import { CommonDBOptions, CommonDBReadOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib'
3
- import { CommonLogger, NumberOfSeconds, ObjectWithId } from '@naturalcycles/js-lib'
2
+ import type {
3
+ CommonDBOptions,
4
+ CommonDBReadOptions,
5
+ CommonDBSaveOptions,
6
+ } from '@naturalcycles/db-lib'
7
+ import type { CommonLogger, NumberOfSeconds, ObjectWithId } from '@naturalcycles/js-lib'
4
8
 
5
9
  export interface DatastorePayload<T = any> {
6
10
  key: Key
@@ -1,14 +1,10 @@
1
- import {
2
- CommonKeyValueDB,
3
- commonKeyValueDBFullSupport,
4
- DBQuery,
5
- KeyValueDBTuple,
6
- } from '@naturalcycles/db-lib'
7
- import { IncrementTuple } from '@naturalcycles/db-lib/dist/kv/commonKeyValueDB'
8
- import { AppError, ObjectWithId } from '@naturalcycles/js-lib'
9
- import { ReadableTyped } from '@naturalcycles/nodejs-lib'
10
- import { DatastoreDB } from './datastore.db'
11
- import { DatastoreDBCfg } from './datastore.model'
1
+ import type { CommonKeyValueDB, IncrementTuple, KeyValueDBTuple } from '@naturalcycles/db-lib'
2
+ import { commonKeyValueDBFullSupport, DBQuery } from '@naturalcycles/db-lib'
3
+ import type { ObjectWithId } from '@naturalcycles/js-lib'
4
+ import { AppError } from '@naturalcycles/js-lib'
5
+ import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
6
+ import { DatastoreDB } from './datastore.db.js'
7
+ import type { DatastoreDBCfg } from './datastore.model.js'
12
8
 
13
9
  interface KVObject {
14
10
  id: string
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from './datastore.db'
2
- export * from './datastore.model'
3
- export * from './datastoreKeyValueDB'
1
+ export * from './datastore.db.js'
2
+ export * from './datastore.model.js'
3
+ export * from './datastoreKeyValueDB.js'
package/src/query.util.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { PropertyFilter, Query } from '@google-cloud/datastore'
2
- import { DBQuery, DBQueryFilterOperator } from '@naturalcycles/db-lib'
3
- import { ObjectWithId, StringMap } from '@naturalcycles/js-lib'
1
+ import type { PropertyFilter, Query } from '@google-cloud/datastore'
2
+ import type { DBQuery, DBQueryFilterOperator } from '@naturalcycles/db-lib'
3
+ import type { ObjectWithId, StringMap } from '@naturalcycles/js-lib'
4
4
 
5
5
  const FNAME_MAP: StringMap = {
6
6
  id: '__key__',
@@ -15,6 +15,7 @@ const OP_MAP: Partial<Record<DBQueryFilterOperator, string>> = {
15
15
  export function dbQueryToDatastoreQuery<ROW extends ObjectWithId>(
16
16
  dbQuery: Readonly<DBQuery<ROW>>,
17
17
  emptyQuery: Query,
18
+ propertyFilterClass: typeof PropertyFilter,
18
19
  ): Query {
19
20
  let q = emptyQuery
20
21
 
@@ -30,7 +31,7 @@ export function dbQueryToDatastoreQuery<ROW extends ObjectWithId>(
30
31
  // `a == null` will return just that - rows with null values
31
32
  let { op, val } = f
32
33
  if (val === undefined) val = null
33
- q = q.filter(new PropertyFilter(f.name as string, OP_MAP[op] || (op as any), val))
34
+ q = q.filter(new propertyFilterClass(f.name as string, OP_MAP[op] || (op as any), val))
34
35
  }
35
36
 
36
37
  // limit