@naturalcycles/db-lib 8.47.0 → 8.48.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.
@@ -23,8 +23,6 @@ export declare class CacheDB extends BaseCommonDB implements CommonDB {
23
23
  getTables(): Promise<string[]>;
24
24
  getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
25
25
  createTable<ROW extends ObjectWithId>(table: string, schema: JsonSchemaObject<ROW>, opt?: CacheDBCreateOptions): Promise<void>;
26
- getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], opt?: CacheDBSaveOptions<ROW>): Promise<ROW[]>;
27
- deleteByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], opt?: CacheDBOptions): Promise<number>;
28
26
  saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: CacheDBSaveOptions<ROW>): Promise<void>;
29
27
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBSaveOptions<ROW>): Promise<RunQueryResult<ROW>>;
30
28
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBOptions): Promise<number>;
@@ -42,57 +42,6 @@ class CacheDB extends base_common_db_1.BaseCommonDB {
42
42
  await this.cfg.cacheDB.createTable(table, schema, opt);
43
43
  }
44
44
  }
45
- async getByIds(table, ids, opt = {}) {
46
- const resultMap = {};
47
- const missingIds = [];
48
- if (!opt.skipCache && !this.cfg.skipCache) {
49
- const results = await this.cfg.cacheDB.getByIds(table, ids, opt);
50
- results.forEach(r => (resultMap[r.id] = r));
51
- missingIds.push(...ids.filter(id => !resultMap[id]));
52
- if (this.cfg.logCached) {
53
- this.cfg.logger?.log(`${table}.getByIds ${results.length} rows from cache: [${results
54
- .map(r => r.id)
55
- .join(', ')}]`);
56
- }
57
- }
58
- if (missingIds.length && !opt.onlyCache && !this.cfg.onlyCache) {
59
- const results = await this.cfg.downstreamDB.getByIds(table, missingIds, opt);
60
- results.forEach(r => (resultMap[r.id] = r));
61
- if (this.cfg.logDownstream) {
62
- this.cfg.logger?.log(`${table}.getByIds ${results.length} rows from downstream: [${results
63
- .map(r => r.id)
64
- .join(', ')}]`);
65
- }
66
- if (!opt.skipCache) {
67
- const cacheResult = this.cfg.cacheDB.saveBatch(table, results, opt);
68
- if (this.cfg.awaitCache)
69
- await cacheResult;
70
- }
71
- }
72
- // return in right order
73
- return ids.map(id => resultMap[id]).filter(Boolean);
74
- }
75
- async deleteByIds(table, ids, opt = {}) {
76
- let deletedIds = 0;
77
- if (!opt.onlyCache && !this.cfg.onlyCache) {
78
- deletedIds = await this.cfg.downstreamDB.deleteByIds(table, ids, opt);
79
- if (this.cfg.logDownstream) {
80
- this.cfg.logger?.log(`${table}.deleteByIds ${deletedIds} rows from downstream`);
81
- }
82
- }
83
- if (!opt.skipCache && !this.cfg.skipCache) {
84
- const cacheResult = this.cfg.cacheDB
85
- .deleteByIds(table, ids, opt)
86
- .then(deletedFromCache => {
87
- if (this.cfg.logCached) {
88
- this.cfg.logger?.log(`${table}.deleteByIds ${deletedFromCache} rows from cache`);
89
- }
90
- });
91
- if (this.cfg.awaitCache)
92
- await cacheResult;
93
- }
94
- return deletedIds;
95
- }
96
45
  async saveBatch(table, rows, opt = {}) {
97
46
  if (!opt.onlyCache && !this.cfg.onlyCache) {
98
47
  await this.cfg.downstreamDB.saveBatch(table, rows, opt);
@@ -21,7 +21,6 @@ export declare class FileDB extends BaseCommonDB implements CommonDB {
21
21
  cfg: FileDBCfg;
22
22
  ping(): Promise<void>;
23
23
  getTables(): Promise<string[]>;
24
- getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], _opt?: CommonDBOptions): Promise<ROW[]>;
25
24
  saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
26
25
  /**
27
26
  * Implementation is optimized for loading/saving _whole files_.
@@ -30,7 +29,6 @@ export declare class FileDB extends BaseCommonDB implements CommonDB {
30
29
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<RunQueryResult<ROW>>;
31
30
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
32
31
  streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CommonDBStreamOptions): ReadableTyped<ROW>;
33
- deleteByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], _opt?: CommonDBOptions): Promise<number>;
34
32
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
35
33
  getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
36
34
  loadFile<ROW extends ObjectWithId>(table: string): Promise<ROW[]>;
@@ -34,10 +34,6 @@ class FileDB extends __1.BaseCommonDB {
34
34
  this.logFinished(started, `getTables() ${tables.length} tables`);
35
35
  return tables;
36
36
  }
37
- async getByIds(table, ids, _opt) {
38
- const byId = (0, js_lib_1._by)(await this.loadFile(table), r => r.id);
39
- return ids.map(id => byId[id]).filter(Boolean);
40
- }
41
37
  async saveBatch(table, rows, _opt) {
42
38
  if (!rows.length)
43
39
  return; // save some api calls
@@ -130,22 +126,6 @@ class FileDB extends __1.BaseCommonDB {
130
126
  });
131
127
  return readable;
132
128
  }
133
- async deleteByIds(table, ids, _opt) {
134
- if (!ids.length)
135
- return 0;
136
- let deleted = 0;
137
- const rows = (await this.loadFile(table)).filter(r => {
138
- if (ids.includes(r.id)) {
139
- deleted++;
140
- return false;
141
- }
142
- return true;
143
- });
144
- if (deleted > 0) {
145
- await this.saveFile(table, rows);
146
- }
147
- return deleted;
148
- }
149
129
  async deleteByQuery(q, _opt) {
150
130
  const byId = (0, js_lib_1._by)(await this.loadFile(q.table), r => r.id);
151
131
  let deleted = 0;
@@ -52,9 +52,7 @@ export declare class InMemoryDB implements CommonDB {
52
52
  getTables(): Promise<string[]>;
53
53
  getTableSchema<ROW extends ObjectWithId>(_table: string): Promise<JsonSchemaRootObject<ROW>>;
54
54
  createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>, opt?: CommonDBCreateOptions): Promise<void>;
55
- getByIds<ROW extends ObjectWithId>(_table: string, ids: ROW['id'][], _opt?: CommonDBOptions): Promise<ROW[]>;
56
55
  saveBatch<ROW extends Partial<ObjectWithId>>(_table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
57
- deleteByIds<ROW extends ObjectWithId>(_table: string, ids: ROW['id'][], _opt?: CommonDBOptions): Promise<number>;
58
56
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
59
57
  updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>): Promise<number>;
60
58
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<RunQueryResult<ROW>>;
@@ -8,6 +8,7 @@ const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
8
8
  const colors_1 = require("@naturalcycles/nodejs-lib/dist/colors");
9
9
  const fs = require("fs-extra");
10
10
  const __1 = require("../..");
11
+ const dbQuery_1 = require("../../query/dbQuery");
11
12
  class InMemoryDB {
12
13
  constructor(cfg) {
13
14
  // data[table][id] > {id: 'a', created: ... }
@@ -66,11 +67,6 @@ class InMemoryDB {
66
67
  this.data[table] ||= {};
67
68
  }
68
69
  }
69
- async getByIds(_table, ids, _opt) {
70
- const table = this.cfg.tablesPrefix + _table;
71
- this.data[table] ||= {};
72
- return ids.map(id => this.data[table][id]).filter(Boolean);
73
- }
74
70
  async saveBatch(_table, rows, opt = {}) {
75
71
  const table = this.cfg.tablesPrefix + _table;
76
72
  this.data[table] ||= {};
@@ -91,24 +87,18 @@ class InMemoryDB {
91
87
  this.data[table][r.id] = JSON.parse(JSON.stringify(r), nodejs_lib_1.bufferReviver);
92
88
  });
93
89
  }
94
- async deleteByIds(_table, ids, _opt) {
95
- const table = this.cfg.tablesPrefix + _table;
90
+ async deleteByQuery(q, _opt) {
91
+ const table = this.cfg.tablesPrefix + q.table;
96
92
  this.data[table] ||= {};
97
93
  let count = 0;
98
- ids.forEach(id => {
99
- if (!this.data[table][id])
94
+ (0, __1.queryInMemory)(q, Object.values(this.data[table] || {})).forEach(r => {
95
+ if (!this.data[table][r.id])
100
96
  return;
101
- delete this.data[table][id];
97
+ delete this.data[table][r.id];
102
98
  count++;
103
99
  });
104
100
  return count;
105
101
  }
106
- async deleteByQuery(q, _opt) {
107
- const table = this.cfg.tablesPrefix + q.table;
108
- const rows = (0, __1.queryInMemory)(q, Object.values(this.data[table] || {}));
109
- const ids = rows.map(r => r.id);
110
- return await this.deleteByIds(q.table, ids);
111
- }
112
102
  async updateByQuery(q, patch) {
113
103
  const patchEntries = Object.entries(patch);
114
104
  if (!patchEntries.length)
@@ -147,7 +137,10 @@ class InMemoryDB {
147
137
  await this.saveBatch(op.table, op.rows, { ...op.opt, ...opt });
148
138
  }
149
139
  else if (op.type === 'deleteByIds') {
150
- await this.deleteByIds(op.table, op.ids, { ...op.opt, ...opt });
140
+ await this.deleteByQuery(dbQuery_1.DBQuery.create(op.table).filter('id', 'in', op.ids), {
141
+ ...op.opt,
142
+ ...opt,
143
+ });
151
144
  }
152
145
  else {
153
146
  throw new Error(`DBOperation not supported: ${op.type}`);
@@ -17,10 +17,6 @@ const FILTER_FNS = {
17
17
  // Important: q.table is not used in this function, so tablesPrefix is not needed.
18
18
  // But should be careful here..
19
19
  function queryInMemory(q, rows = []) {
20
- // .ids
21
- if (q._ids?.length) {
22
- rows = rows.filter(r => q._ids.includes(r.id));
23
- }
24
20
  // .filter
25
21
  // eslint-disable-next-line unicorn/no-array-reduce
26
22
  rows = q._filters.reduce((rows, filter) => {
@@ -13,10 +13,8 @@ export declare class BaseCommonDB implements CommonDB {
13
13
  getTables(): Promise<string[]>;
14
14
  getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
15
15
  createTable<ROW extends ObjectWithId>(table: string, schema: JsonSchemaObject<ROW>): Promise<void>;
16
- deleteByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][]): Promise<number>;
17
16
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number>;
18
17
  updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>, opt?: CommonDBOptions): Promise<number>;
19
- getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][]): Promise<ROW[]>;
20
18
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<RunQueryResult<ROW>>;
21
19
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number>;
22
20
  saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
@@ -17,10 +17,7 @@ class BaseCommonDB {
17
17
  throw new Error('getTableSchema is not implemented');
18
18
  }
19
19
  async createTable(table, schema) {
20
- throw new Error('createTable is not implemented');
21
- }
22
- async deleteByIds(table, ids) {
23
- throw new Error('deleteByIds is not implemented');
20
+ // no-op
24
21
  }
25
22
  async deleteByQuery(q) {
26
23
  throw new Error('deleteByQuery is not implemented');
@@ -28,9 +25,6 @@ class BaseCommonDB {
28
25
  async updateByQuery(q, patch, opt) {
29
26
  throw new Error('updateByQuery is not implemented');
30
27
  }
31
- async getByIds(table, ids) {
32
- throw new Error('getByIds is not implemented');
33
- }
34
28
  async runQuery(q) {
35
29
  throw new Error('runQuery is not implemented');
36
30
  }
@@ -27,11 +27,6 @@ export interface CommonDB {
27
27
  * Caution! dropIfExists defaults to false. If set to true - will actually DROP the table!
28
28
  */
29
29
  createTable<ROW extends ObjectWithId>(table: string, schema: JsonSchemaObject<ROW>, opt?: CommonDBCreateOptions): Promise<void>;
30
- /**
31
- * Order of items returned is not guaranteed to match order of ids.
32
- * (Such limitation exists because Datastore doesn't support it).
33
- */
34
- getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], opt?: CommonDBOptions): Promise<ROW[]>;
35
30
  /**
36
31
  * Order by 'id' is not supported by all implementations (for example, Datastore doesn't support it).
37
32
  */
@@ -46,7 +41,6 @@ export interface CommonDB {
46
41
  * Returns number of deleted items.
47
42
  * Not supported by all implementations (e.g Datastore will always return same number as number of ids).
48
43
  */
49
- deleteByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], opt?: CommonDBOptions): Promise<number>;
50
44
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CommonDBOptions): Promise<number>;
51
45
  /**
52
46
  * Applies patch to the rows returned by the query.
@@ -112,13 +112,15 @@ class CommonDao {
112
112
  let dbm;
113
113
  if (opt.timeout) {
114
114
  // todo: possibly remove it after debugging is done
115
- dbm = (await (0, js_lib_1.pTimeout)(() => this.cfg.db.getByIds(table, [id]), {
115
+ dbm = await (0, js_lib_1.pTimeout)(async () => {
116
+ return (await this.cfg.db.runQuery(dbQuery_1.DBQuery.create(table).filterEq('id', id))).rows[0];
117
+ }, {
116
118
  timeout: opt.timeout,
117
119
  name: `getById(${table})`,
118
- }))[0];
120
+ });
119
121
  }
120
122
  else {
121
- dbm = (await this.cfg.db.getByIds(table, [id]))[0];
123
+ dbm = (await this.cfg.db.runQuery(dbQuery_1.DBQuery.create(table).filterEq('id', id))).rows[0];
122
124
  }
123
125
  const bm = opt.raw ? dbm : await this.dbmToBM(dbm, opt);
124
126
  this.logResult(started, op, bm, table);
@@ -143,7 +145,7 @@ class CommonDao {
143
145
  const op = `getByIdAsDBM(${id})`;
144
146
  const table = opt.table || this.cfg.table;
145
147
  const started = this.logStarted(op, table);
146
- let [dbm] = await this.cfg.db.getByIds(table, [id]);
148
+ let [dbm] = (await this.cfg.db.runQuery(dbQuery_1.DBQuery.create(table).filterEq('id', id))).rows;
147
149
  if (!opt.raw) {
148
150
  dbm = this.anyToDBM(dbm, opt);
149
151
  }
@@ -156,7 +158,7 @@ class CommonDao {
156
158
  const op = `getByIdAsTM(${id})`;
157
159
  const table = opt.table || this.cfg.table;
158
160
  const started = this.logStarted(op, table);
159
- const [dbm] = await this.cfg.db.getByIds(table, [id]);
161
+ const [dbm] = (await this.cfg.db.runQuery(dbQuery_1.DBQuery.create(table).filterEq('id', id))).rows;
160
162
  if (opt.raw) {
161
163
  this.logResult(started, op, dbm, table);
162
164
  return dbm || null;
@@ -170,7 +172,7 @@ class CommonDao {
170
172
  const op = `getByIds ${ids.length} id(s) (${(0, js_lib_1._truncate)(ids.slice(0, 10).join(', '), 50)})`;
171
173
  const table = opt.table || this.cfg.table;
172
174
  const started = this.logStarted(op, table);
173
- const dbms = await this.cfg.db.getByIds(table, ids);
175
+ const dbms = (await this.cfg.db.runQuery(dbQuery_1.DBQuery.create(table).filterIn('id', ids))).rows;
174
176
  const bms = opt.raw ? dbms : await this.dbmsToBM(dbms, opt);
175
177
  this.logResult(started, op, bms, table);
176
178
  return bms;
@@ -179,7 +181,7 @@ class CommonDao {
179
181
  const op = `getByIdsAsDBM ${ids.length} id(s) (${(0, js_lib_1._truncate)(ids.slice(0, 10).join(', '), 50)})`;
180
182
  const table = opt.table || this.cfg.table;
181
183
  const started = this.logStarted(op, table);
182
- const dbms = await this.cfg.db.getByIds(table, ids);
184
+ const dbms = (await this.cfg.db.runQuery(dbQuery_1.DBQuery.create(table).filterIn('id', ids))).rows;
183
185
  this.logResult(started, op, dbms, table);
184
186
  return dbms;
185
187
  }
@@ -229,7 +231,8 @@ class CommonDao {
229
231
  }
230
232
  async ensureUniqueId(table, dbm) {
231
233
  // todo: retry N times
232
- const existing = await this.cfg.db.getByIds(table, [dbm.id]);
234
+ const existing = (await this.cfg.db.runQuery(dbQuery_1.DBQuery.create(table).filterEq('id', dbm.id)))
235
+ .rows;
233
236
  if (existing.length) {
234
237
  throw new js_lib_1.AppError(cnst_1.DBLibError.NON_UNIQUE_ID, {
235
238
  table,
@@ -656,9 +659,9 @@ class CommonDao {
656
659
  const op = `deleteById(${id})`;
657
660
  const table = opt.table || this.cfg.table;
658
661
  const started = this.logStarted(op, table);
659
- const ids = await this.cfg.db.deleteByIds(table, [id]);
662
+ const count = await this.cfg.db.deleteByQuery(dbQuery_1.DBQuery.create(table).filterEq('id', id));
660
663
  this.logSaveResult(started, op, table);
661
- return ids;
664
+ return count;
662
665
  }
663
666
  async deleteByIds(ids, opt = {}) {
664
667
  this.requireWriteAccess();
@@ -666,9 +669,9 @@ class CommonDao {
666
669
  const op = `deleteByIds(${ids.join(', ')})`;
667
670
  const table = opt.table || this.cfg.table;
668
671
  const started = this.logStarted(op, table);
669
- const deletedIds = await this.cfg.db.deleteByIds(table, ids);
672
+ const count = await this.cfg.db.deleteByQuery(dbQuery_1.DBQuery.create(table).filterIn('id', ids));
670
673
  this.logSaveResult(started, op, table);
671
- return deletedIds;
674
+ return count;
672
675
  }
673
676
  /**
674
677
  * Pass `stream: true` option to use Streaming: it will Stream the query, batch by 500, and execute
@@ -691,7 +694,7 @@ class CommonDao {
691
694
  }),
692
695
  (0, nodejs_lib_1.transformBuffer)({ batchSize }),
693
696
  (0, nodejs_lib_1.transformMap)(async (ids) => {
694
- deleted += await this.cfg.db.deleteByIds(q.table, ids, opt);
697
+ deleted += await this.cfg.db.deleteByQuery(dbQuery_1.DBQuery.create(q.table).filterIn('id', ids), opt);
695
698
  }, {
696
699
  predicate: js_lib_1._passthroughPredicate,
697
700
  }),
@@ -712,10 +715,10 @@ class CommonDao {
712
715
  return deleted;
713
716
  }
714
717
  async updateById(id, patch, opt = {}) {
715
- return await this.updateByQuery(this.query().byId(id), patch, opt);
718
+ return await this.updateByQuery(this.query().filterEq('id', id), patch, opt);
716
719
  }
717
720
  async updateByIds(ids, patch, opt = {}) {
718
- return await this.updateByQuery(this.query().byIds(ids), patch, opt);
721
+ return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt);
719
722
  }
720
723
  async updateByQuery(q, patch, opt = {}) {
721
724
  this.requireWriteAccess();
@@ -66,9 +66,9 @@ export declare class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
66
66
  _selectedFieldNames?: (keyof ROW)[];
67
67
  _groupByFieldNames?: (keyof ROW)[];
68
68
  _distinct: boolean;
69
- _ids?: ROW['id'][];
70
69
  filter(name: keyof ROW, op: DBQueryFilterOperator, val: any): this;
71
70
  filterEq(name: keyof ROW, val: any): this;
71
+ filterIn(name: keyof ROW, val: any[]): this;
72
72
  limit(limit: number): this;
73
73
  offset(offset: number): this;
74
74
  order(name: keyof ROW, descending?: boolean): this;
@@ -77,20 +77,6 @@ export declare class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
77
77
  distinct(distinct?: boolean): this;
78
78
  startCursor(startCursor?: string): this;
79
79
  endCursor(endCursor?: string): this;
80
- /**
81
- * Allows to query by ids (one or many).
82
- * Similar to:
83
- * SELECT * FROM table where id in (a, b, c)
84
- * or (if only 1 id is passed)
85
- * SELECT * FROM table where id = a
86
- */
87
- byIds(ids: ROW['id'][]): this;
88
- /**
89
- * Allows to query by id.
90
- * Similar to:
91
- * SELECT * FROM table where id = a
92
- */
93
- byId(id: ROW['id']): this;
94
80
  clone(): DBQuery<ROW>;
95
81
  pretty(): string;
96
82
  prettyConditions(): string[];
@@ -50,6 +50,10 @@ class DBQuery {
50
50
  this._filters.push({ name, op: '==', val });
51
51
  return this;
52
52
  }
53
+ filterIn(name, val) {
54
+ this._filters.push({ name, op: 'in', val });
55
+ return this;
56
+ }
53
57
  limit(limit) {
54
58
  this._limitValue = limit;
55
59
  return this;
@@ -85,26 +89,6 @@ class DBQuery {
85
89
  this._endCursor = endCursor;
86
90
  return this;
87
91
  }
88
- /**
89
- * Allows to query by ids (one or many).
90
- * Similar to:
91
- * SELECT * FROM table where id in (a, b, c)
92
- * or (if only 1 id is passed)
93
- * SELECT * FROM table where id = a
94
- */
95
- byIds(ids) {
96
- this._ids = ids;
97
- return this;
98
- }
99
- /**
100
- * Allows to query by id.
101
- * Similar to:
102
- * SELECT * FROM table where id = a
103
- */
104
- byId(id) {
105
- this._ids = [id];
106
- return this;
107
- }
108
92
  clone() {
109
93
  return (0, js_lib_1._objectAssign)(new DBQuery(this.table), {
110
94
  _filters: [...this._filters],
@@ -116,7 +100,6 @@ class DBQuery {
116
100
  _distinct: this._distinct,
117
101
  _startCursor: this._startCursor,
118
102
  _endCursor: this._endCursor,
119
- _ids: this._ids,
120
103
  });
121
104
  }
122
105
  pretty() {
@@ -130,14 +113,6 @@ class DBQuery {
130
113
  if (this._selectedFieldNames) {
131
114
  tokens.push(`select${this._distinct ? ' distinct' : ''}(${this._selectedFieldNames.join(',')})`);
132
115
  }
133
- if (this._ids?.length) {
134
- if (this._ids.length === 1) {
135
- tokens.push(`id=${this._ids[0]}`);
136
- }
137
- else {
138
- tokens.push(`ids in (${this._ids.join(',')})`);
139
- }
140
- }
141
116
  tokens.push(...this._filters.map(f => `${f.name}${f.op}${f.val}`), ...this._orders.map(o => `order by ${o.name}${o.descending ? ' desc' : ''}`));
142
117
  if (this._groupByFieldNames) {
143
118
  tokens.push(`groupBy(${this._groupByFieldNames.join(',')})`);
@@ -48,7 +48,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
48
48
  // DELETE ALL initially
49
49
  test('deleteByIds test items', async () => {
50
50
  const rows = await dao.query().select(['id']).runQuery();
51
- await db.deleteByIds(test_model_1.TEST_TABLE, rows.map(i => i.id));
51
+ await db.deleteByQuery(__1.DBQuery.create(test_model_1.TEST_TABLE).filter('id', 'in', rows.map(r => r.id)));
52
52
  });
53
53
  // QUERY empty
54
54
  test('runQuery(all), runQueryCount should return empty', async () => {
@@ -114,7 +114,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
114
114
  // GET not empty
115
115
  test('getByIds all items', async () => {
116
116
  const rows = await dao.getByIds(items.map(i => i.id).concat('abcd'));
117
- (0, dbTest_1.expectMatch)(expectedItems, rows, quirks);
117
+ (0, dbTest_1.expectMatch)(expectedItems, (0, js_lib_1._sortBy)(rows, r => r.id), quirks);
118
118
  });
119
119
  // QUERY
120
120
  if (querying) {
@@ -187,8 +187,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
187
187
  });
188
188
  test('cleanup', async () => {
189
189
  // CLEAN UP
190
- const rows = await dao.query().select(['id']).runQuery();
191
- await db.deleteByIds(test_model_1.TEST_TABLE, rows.map(i => i.id));
190
+ await dao.query().deleteByQuery();
192
191
  });
193
192
  }
194
193
  if (transactions) {
@@ -37,7 +37,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
37
37
  // DELETE ALL initially
38
38
  test('deleteByIds test items', async () => {
39
39
  const { rows } = await db.runQuery(queryAll().select(['id']));
40
- await db.deleteByIds(test_model_1.TEST_TABLE, rows.map(i => i.id));
40
+ await db.deleteByQuery(queryAll().filterIn('id', rows.map(i => i.id)));
41
41
  });
42
42
  // QUERY empty
43
43
  test('runQuery(all), runQueryCount should return empty', async () => {
@@ -49,15 +49,15 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
49
49
  }
50
50
  // GET empty
51
51
  test('getByIds(item1.id) should return empty', async () => {
52
- const [item1Loaded] = await db.getByIds(test_model_1.TEST_TABLE, [item1.id]);
52
+ const [item1Loaded] = (await db.runQuery(queryAll().filterEq('id', item1.id))).rows;
53
53
  // console.log(a)
54
54
  expect(item1Loaded).toBeUndefined();
55
55
  });
56
56
  test('getByIds([]) should return []', async () => {
57
- expect(await db.getByIds(test_model_1.TEST_TABLE, [])).toEqual([]);
57
+ expect((await db.runQuery(queryAll().filterIn('id', []))).rows).toEqual([]);
58
58
  });
59
59
  test('getByIds(...) should return empty', async () => {
60
- expect(await db.getByIds(test_model_1.TEST_TABLE, ['abc', 'abcd'])).toEqual([]);
60
+ expect((await db.runQuery(queryAll().filterIn('id', ['abc', 'abcd']))).rows).toEqual([]);
61
61
  });
62
62
  // SAVE
63
63
  if (nullValues) {
@@ -68,7 +68,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
68
68
  };
69
69
  (0, test_util_1.deepFreeze)(item3);
70
70
  await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
71
- const item3Loaded = (await db.getByIds(test_model_1.TEST_TABLE, [item3.id]))[0];
71
+ const item3Loaded = (await db.runQuery(queryAll().filterEq('id', item3.id))).rows[0];
72
72
  expectMatch([item3], [item3Loaded], quirks);
73
73
  expect(item3Loaded.k2).toBeNull();
74
74
  });
@@ -83,7 +83,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
83
83
  const expected = { ...item3 };
84
84
  delete expected.k2;
85
85
  await db.saveBatch(test_model_1.TEST_TABLE, [item3]);
86
- const item3Loaded = (await db.getByIds(test_model_1.TEST_TABLE, [item3.id]))[0];
86
+ const item3Loaded = (await db.runQuery(queryAll().filterEq('id', item3.id))).rows[0];
87
87
  expectMatch([expected], [item3Loaded], quirks);
88
88
  expect(item3Loaded.k2).toBeUndefined();
89
89
  expect(Object.keys(item3Loaded)).not.toContain('k2');
@@ -112,8 +112,9 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
112
112
  }
113
113
  // GET not empty
114
114
  test('getByIds all items', async () => {
115
- const rows = await db.getByIds(test_model_1.TEST_TABLE, items.map(i => i.id).concat('abcd'));
116
- expectMatch(items, rows, quirks);
115
+ const rows = (await db.runQuery(queryAll().filterIn('id', items.map(i => i.id).concat('abcd'))))
116
+ .rows;
117
+ expectMatch(items, (0, js_lib_1._sortBy)(rows, r => r.id), quirks);
117
118
  });
118
119
  // QUERY
119
120
  if (querying) {
@@ -204,7 +205,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
204
205
  b1,
205
206
  };
206
207
  await db.saveBatch(test_model_1.TEST_TABLE, [item]);
207
- const [loaded] = await db.getByIds(test_model_1.TEST_TABLE, [item.id]);
208
+ const loaded = (await db.runQuery(queryAll().filterEq('id', item.id))).rows[0];
208
209
  const b1Loaded = loaded.b1;
209
210
  // console.log({
210
211
  // b11: typeof b1,
@@ -247,7 +248,6 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
247
248
  });
248
249
  }
249
250
  if (updateByQuery) {
250
- // todo: query by ids (same as getByIds)
251
251
  test('updateByQuery simple', async () => {
252
252
  // cleanup, reset initial data
253
253
  await db.deleteByQuery(queryAll());
@@ -290,8 +290,7 @@ function runCommonDBTest(db, features = {}, quirks = {}) {
290
290
  if (querying) {
291
291
  test('cleanup', async () => {
292
292
  // CLEAN UP
293
- const { rows } = await db.runQuery(queryAll().select(['id']));
294
- await db.deleteByIds(test_model_1.TEST_TABLE, rows.map(i => i.id));
293
+ await db.deleteByQuery(queryAll());
295
294
  });
296
295
  }
297
296
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.commitDBTransactionSimple = exports.mergeDBOperations = void 0;
4
+ const dbQuery_1 = require("../query/dbQuery");
4
5
  /**
5
6
  * Optimizes the Transaction (list of DBOperations) to do less operations.
6
7
  * E.g if you save id1 first and then delete it - this function will turn it into a no-op (self-eliminate).
@@ -74,7 +75,10 @@ async function commitDBTransactionSimple(db, tx, opt) {
74
75
  await db.saveBatch(op.table, op.rows, { ...op.opt, ...opt });
75
76
  }
76
77
  else if (op.type === 'deleteByIds') {
77
- await db.deleteByIds(op.table, op.ids, { ...op.opt, ...opt });
78
+ await db.deleteByQuery(dbQuery_1.DBQuery.create(op.table).filter('id', 'in', op.ids), {
79
+ ...op.opt,
80
+ ...opt,
81
+ });
78
82
  }
79
83
  else {
80
84
  throw new Error(`DBOperation not supported: ${op.type}`);
package/package.json CHANGED
@@ -41,7 +41,7 @@
41
41
  "engines": {
42
42
  "node": ">=14.15"
43
43
  },
44
- "version": "8.47.0",
44
+ "version": "8.48.1",
45
45
  "description": "Lowest Common Denominator API to supported Databases",
46
46
  "keywords": [
47
47
  "db",
@@ -1,10 +1,5 @@
1
1
  import { Readable } from 'node:stream'
2
- import {
3
- JsonSchemaObject,
4
- JsonSchemaRootObject,
5
- ObjectWithId,
6
- StringMap,
7
- } from '@naturalcycles/js-lib'
2
+ import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib'
8
3
  import { BaseCommonDB } from '../../base.common.db'
9
4
  import { CommonDB } from '../../common.db'
10
5
  import { CommonDBOptions, DBPatch, RunQueryResult } from '../../db.model'
@@ -72,81 +67,6 @@ export class CacheDB extends BaseCommonDB implements CommonDB {
72
67
  }
73
68
  }
74
69
 
75
- override async getByIds<ROW extends ObjectWithId>(
76
- table: string,
77
- ids: ROW['id'][],
78
- opt: CacheDBSaveOptions<ROW> = {},
79
- ): Promise<ROW[]> {
80
- const resultMap: StringMap<ROW> = {}
81
- const missingIds: ROW['id'][] = []
82
-
83
- if (!opt.skipCache && !this.cfg.skipCache) {
84
- const results = await this.cfg.cacheDB.getByIds<ROW>(table, ids, opt)
85
-
86
- results.forEach(r => (resultMap[r.id] = r))
87
-
88
- missingIds.push(...ids.filter(id => !resultMap[id]))
89
-
90
- if (this.cfg.logCached) {
91
- this.cfg.logger?.log(
92
- `${table}.getByIds ${results.length} rows from cache: [${results
93
- .map(r => r.id)
94
- .join(', ')}]`,
95
- )
96
- }
97
- }
98
-
99
- if (missingIds.length && !opt.onlyCache && !this.cfg.onlyCache) {
100
- const results = await this.cfg.downstreamDB.getByIds<ROW>(table, missingIds, opt)
101
- results.forEach(r => (resultMap[r.id] = r))
102
-
103
- if (this.cfg.logDownstream) {
104
- this.cfg.logger?.log(
105
- `${table}.getByIds ${results.length} rows from downstream: [${results
106
- .map(r => r.id)
107
- .join(', ')}]`,
108
- )
109
- }
110
-
111
- if (!opt.skipCache) {
112
- const cacheResult = this.cfg.cacheDB.saveBatch(table, results, opt)
113
- if (this.cfg.awaitCache) await cacheResult
114
- }
115
- }
116
-
117
- // return in right order
118
- return ids.map(id => resultMap[id]!).filter(Boolean)
119
- }
120
-
121
- override async deleteByIds<ROW extends ObjectWithId>(
122
- table: string,
123
- ids: ROW['id'][],
124
- opt: CacheDBOptions = {},
125
- ): Promise<number> {
126
- let deletedIds = 0
127
-
128
- if (!opt.onlyCache && !this.cfg.onlyCache) {
129
- deletedIds = await this.cfg.downstreamDB.deleteByIds<ROW>(table, ids, opt)
130
-
131
- if (this.cfg.logDownstream) {
132
- this.cfg.logger?.log(`${table}.deleteByIds ${deletedIds} rows from downstream`)
133
- }
134
- }
135
-
136
- if (!opt.skipCache && !this.cfg.skipCache) {
137
- const cacheResult = this.cfg.cacheDB
138
- .deleteByIds<ROW>(table, ids, opt)
139
- .then(deletedFromCache => {
140
- if (this.cfg.logCached) {
141
- this.cfg.logger?.log(`${table}.deleteByIds ${deletedFromCache} rows from cache`)
142
- }
143
- })
144
- if (this.cfg.awaitCache) await cacheResult
145
- }
146
-
147
- return deletedIds
148
- }
149
-
150
70
  override async saveBatch<ROW extends Partial<ObjectWithId>>(
151
71
  table: string,
152
72
  rows: ROW[],
@@ -65,15 +65,6 @@ export class FileDB extends BaseCommonDB implements CommonDB {
65
65
  return tables
66
66
  }
67
67
 
68
- override async getByIds<ROW extends ObjectWithId>(
69
- table: string,
70
- ids: ROW['id'][],
71
- _opt?: CommonDBOptions,
72
- ): Promise<ROW[]> {
73
- const byId = _by(await this.loadFile<ROW>(table), r => r.id)
74
- return ids.map(id => byId[id]!).filter(Boolean)
75
- }
76
-
77
68
  override async saveBatch<ROW extends Partial<ObjectWithId>>(
78
69
  table: string,
79
70
  rows: ROW[],
@@ -198,29 +189,6 @@ export class FileDB extends BaseCommonDB implements CommonDB {
198
189
  return readable
199
190
  }
200
191
 
201
- override async deleteByIds<ROW extends ObjectWithId>(
202
- table: string,
203
- ids: ROW['id'][],
204
- _opt?: CommonDBOptions,
205
- ): Promise<number> {
206
- if (!ids.length) return 0
207
-
208
- let deleted = 0
209
- const rows = (await this.loadFile<ROW>(table)).filter(r => {
210
- if (ids.includes(r.id)) {
211
- deleted++
212
- return false
213
- }
214
- return true
215
- })
216
-
217
- if (deleted > 0) {
218
- await this.saveFile(table, rows)
219
- }
220
-
221
- return deleted
222
- }
223
-
224
192
  override async deleteByQuery<ROW extends ObjectWithId>(
225
193
  q: DBQuery<ROW>,
226
194
  _opt?: CommonDBOptions,
@@ -143,16 +143,6 @@ export class InMemoryDB implements CommonDB {
143
143
  }
144
144
  }
145
145
 
146
- async getByIds<ROW extends ObjectWithId>(
147
- _table: string,
148
- ids: ROW['id'][],
149
- _opt?: CommonDBOptions,
150
- ): Promise<ROW[]> {
151
- const table = this.cfg.tablesPrefix + _table
152
- this.data[table] ||= {}
153
- return ids.map(id => this.data[table]![id] as ROW).filter(Boolean)
154
- }
155
-
156
146
  async saveBatch<ROW extends Partial<ObjectWithId>>(
157
147
  _table: string,
158
148
  rows: ROW[],
@@ -184,32 +174,21 @@ export class InMemoryDB implements CommonDB {
184
174
  })
185
175
  }
186
176
 
187
- async deleteByIds<ROW extends ObjectWithId>(
188
- _table: string,
189
- ids: ROW['id'][],
177
+ async deleteByQuery<ROW extends ObjectWithId>(
178
+ q: DBQuery<ROW>,
190
179
  _opt?: CommonDBOptions,
191
180
  ): Promise<number> {
192
- const table = this.cfg.tablesPrefix + _table
181
+ const table = this.cfg.tablesPrefix + q.table
193
182
  this.data[table] ||= {}
194
183
  let count = 0
195
- ids.forEach(id => {
196
- if (!this.data[table]![id]) return
197
- delete this.data[table]![id]
184
+ queryInMemory(q, Object.values(this.data[table] || {}) as ROW[]).forEach(r => {
185
+ if (!this.data[table]![r.id]) return
186
+ delete this.data[table]![r.id]
198
187
  count++
199
188
  })
200
189
  return count
201
190
  }
202
191
 
203
- async deleteByQuery<ROW extends ObjectWithId>(
204
- q: DBQuery<ROW>,
205
- _opt?: CommonDBOptions,
206
- ): Promise<number> {
207
- const table = this.cfg.tablesPrefix + q.table
208
- const rows = queryInMemory(q, Object.values(this.data[table] || {}) as ROW[])
209
- const ids = rows.map(r => r.id)
210
- return await this.deleteByIds(q.table, ids)
211
- }
212
-
213
192
  async updateByQuery<ROW extends ObjectWithId>(
214
193
  q: DBQuery<ROW>,
215
194
  patch: DBPatch<ROW>,
@@ -264,7 +243,10 @@ export class InMemoryDB implements CommonDB {
264
243
  if (op.type === 'saveBatch') {
265
244
  await this.saveBatch(op.table, op.rows, { ...op.opt, ...opt })
266
245
  } else if (op.type === 'deleteByIds') {
267
- await this.deleteByIds(op.table, op.ids, { ...op.opt, ...opt })
246
+ await this.deleteByQuery(DBQuery.create(op.table).filter('id', 'in', op.ids), {
247
+ ...op.opt,
248
+ ...opt,
249
+ })
268
250
  } else {
269
251
  throw new Error(`DBOperation not supported: ${(op as any).type}`)
270
252
  }
@@ -19,11 +19,6 @@ const FILTER_FNS: Record<DBQueryFilterOperator, FilterFn> = {
19
19
  // Important: q.table is not used in this function, so tablesPrefix is not needed.
20
20
  // But should be careful here..
21
21
  export function queryInMemory<ROW extends ObjectWithId>(q: DBQuery<ROW>, rows: ROW[] = []): ROW[] {
22
- // .ids
23
- if (q._ids?.length) {
24
- rows = rows.filter(r => q._ids!.includes(r.id))
25
- }
26
-
27
22
  // .filter
28
23
  // eslint-disable-next-line unicorn/no-array-reduce
29
24
  rows = q._filters.reduce((rows, filter) => {
@@ -30,11 +30,7 @@ export class BaseCommonDB implements CommonDB {
30
30
  table: string,
31
31
  schema: JsonSchemaObject<ROW>,
32
32
  ): Promise<void> {
33
- throw new Error('createTable is not implemented')
34
- }
35
-
36
- async deleteByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][]): Promise<number> {
37
- throw new Error('deleteByIds is not implemented')
33
+ // no-op
38
34
  }
39
35
 
40
36
  async deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<number> {
@@ -49,10 +45,6 @@ export class BaseCommonDB implements CommonDB {
49
45
  throw new Error('updateByQuery is not implemented')
50
46
  }
51
47
 
52
- async getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][]): Promise<ROW[]> {
53
- throw new Error('getByIds is not implemented')
54
- }
55
-
56
48
  async runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>): Promise<RunQueryResult<ROW>> {
57
49
  throw new Error('runQuery is not implemented')
58
50
  }
package/src/common.db.ts CHANGED
@@ -43,17 +43,6 @@ export interface CommonDB {
43
43
  opt?: CommonDBCreateOptions,
44
44
  ): Promise<void>
45
45
 
46
- // GET
47
- /**
48
- * Order of items returned is not guaranteed to match order of ids.
49
- * (Such limitation exists because Datastore doesn't support it).
50
- */
51
- getByIds<ROW extends ObjectWithId>(
52
- table: string,
53
- ids: ROW['id'][],
54
- opt?: CommonDBOptions,
55
- ): Promise<ROW[]>
56
-
57
46
  // QUERY
58
47
  /**
59
48
  * Order by 'id' is not supported by all implementations (for example, Datastore doesn't support it).
@@ -85,12 +74,6 @@ export interface CommonDB {
85
74
  * Returns number of deleted items.
86
75
  * Not supported by all implementations (e.g Datastore will always return same number as number of ids).
87
76
  */
88
- deleteByIds<ROW extends ObjectWithId>(
89
- table: string,
90
- ids: ROW['id'][],
91
- opt?: CommonDBOptions,
92
- ): Promise<number>
93
-
94
77
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CommonDBOptions): Promise<number>
95
78
 
96
79
  /**
@@ -134,14 +134,17 @@ export class CommonDao<
134
134
 
135
135
  if (opt.timeout) {
136
136
  // todo: possibly remove it after debugging is done
137
- dbm = (
138
- await pTimeout(() => this.cfg.db.getByIds<DBM>(table, [id]), {
137
+ dbm = await pTimeout(
138
+ async () => {
139
+ return (await this.cfg.db.runQuery(DBQuery.create<DBM>(table).filterEq('id', id))).rows[0]
140
+ },
141
+ {
139
142
  timeout: opt.timeout,
140
143
  name: `getById(${table})`,
141
- })
142
- )[0]
144
+ },
145
+ )
143
146
  } else {
144
- dbm = (await this.cfg.db.getByIds<DBM>(table, [id]))[0]
147
+ dbm = (await this.cfg.db.runQuery(DBQuery.create<DBM>(table).filterEq('id', id))).rows[0]
145
148
  }
146
149
 
147
150
  const bm = opt.raw ? (dbm as any) : await this.dbmToBM(dbm, opt)
@@ -171,7 +174,7 @@ export class CommonDao<
171
174
  const op = `getByIdAsDBM(${id})`
172
175
  const table = opt.table || this.cfg.table
173
176
  const started = this.logStarted(op, table)
174
- let [dbm] = await this.cfg.db.getByIds<DBM>(table, [id])
177
+ let [dbm] = (await this.cfg.db.runQuery(DBQuery.create<DBM>(table).filterEq('id', id))).rows
175
178
  if (!opt.raw) {
176
179
  dbm = this.anyToDBM(dbm!, opt)
177
180
  }
@@ -186,7 +189,7 @@ export class CommonDao<
186
189
  const op = `getByIdAsTM(${id})`
187
190
  const table = opt.table || this.cfg.table
188
191
  const started = this.logStarted(op, table)
189
- const [dbm] = await this.cfg.db.getByIds<DBM>(table, [id])
192
+ const [dbm] = (await this.cfg.db.runQuery(DBQuery.create<DBM>(table).filterEq('id', id))).rows
190
193
  if (opt.raw) {
191
194
  this.logResult(started, op, dbm, table)
192
195
  return (dbm as any) || null
@@ -201,7 +204,7 @@ export class CommonDao<
201
204
  const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
202
205
  const table = opt.table || this.cfg.table
203
206
  const started = this.logStarted(op, table)
204
- const dbms = await this.cfg.db.getByIds<DBM>(table, ids)
207
+ const dbms = (await this.cfg.db.runQuery(DBQuery.create<DBM>(table).filterIn('id', ids))).rows
205
208
  const bms = opt.raw ? (dbms as any) : await this.dbmsToBM(dbms, opt)
206
209
  this.logResult(started, op, bms, table)
207
210
  return bms
@@ -211,7 +214,7 @@ export class CommonDao<
211
214
  const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
212
215
  const table = opt.table || this.cfg.table
213
216
  const started = this.logStarted(op, table)
214
- const dbms = await this.cfg.db.getByIds<DBM>(table, ids)
217
+ const dbms = (await this.cfg.db.runQuery(DBQuery.create<DBM>(table).filterIn('id', ids))).rows
215
218
  this.logResult(started, op, dbms, table)
216
219
  return dbms
217
220
  }
@@ -267,7 +270,8 @@ export class CommonDao<
267
270
 
268
271
  private async ensureUniqueId(table: string, dbm: DBM): Promise<void> {
269
272
  // todo: retry N times
270
- const existing = await this.cfg.db.getByIds<DBM>(table, [dbm.id])
273
+ const existing = (await this.cfg.db.runQuery(DBQuery.create<DBM>(table).filterEq('id', dbm.id)))
274
+ .rows
271
275
  if (existing.length) {
272
276
  throw new AppError(DBLibError.NON_UNIQUE_ID, {
273
277
  table,
@@ -869,9 +873,9 @@ export class CommonDao<
869
873
  const op = `deleteById(${id})`
870
874
  const table = opt.table || this.cfg.table
871
875
  const started = this.logStarted(op, table)
872
- const ids = await this.cfg.db.deleteByIds(table, [id])
876
+ const count = await this.cfg.db.deleteByQuery(DBQuery.create(table).filterEq('id', id))
873
877
  this.logSaveResult(started, op, table)
874
- return ids
878
+ return count
875
879
  }
876
880
 
877
881
  async deleteByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<number> {
@@ -880,9 +884,9 @@ export class CommonDao<
880
884
  const op = `deleteByIds(${ids.join(', ')})`
881
885
  const table = opt.table || this.cfg.table
882
886
  const started = this.logStarted(op, table)
883
- const deletedIds = await this.cfg.db.deleteByIds(table, ids)
887
+ const count = await this.cfg.db.deleteByQuery(DBQuery.create(table).filterIn('id', ids))
884
888
  this.logSaveResult(started, op, table)
885
- return deletedIds
889
+ return count
886
890
  }
887
891
 
888
892
  /**
@@ -912,7 +916,10 @@ export class CommonDao<
912
916
  transformBuffer<string>({ batchSize }),
913
917
  transformMap<string[], void>(
914
918
  async ids => {
915
- deleted += await this.cfg.db.deleteByIds(q.table, ids, opt)
919
+ deleted += await this.cfg.db.deleteByQuery(
920
+ DBQuery.create(q.table).filterIn('id', ids),
921
+ opt,
922
+ )
916
923
  },
917
924
  {
918
925
  predicate: _passthroughPredicate,
@@ -936,11 +943,11 @@ export class CommonDao<
936
943
  }
937
944
 
938
945
  async updateById(id: ID, patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
939
- return await this.updateByQuery(this.query().byId(id), patch, opt)
946
+ return await this.updateByQuery(this.query().filterEq('id', id), patch, opt)
940
947
  }
941
948
 
942
949
  async updateByIds(ids: ID[], patch: DBPatch<DBM>, opt: CommonDaoOptions = {}): Promise<number> {
943
- return await this.updateByQuery(this.query().byIds(ids), patch, opt)
950
+ return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt)
944
951
  }
945
952
 
946
953
  async updateByQuery(
@@ -113,7 +113,6 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
113
113
  _selectedFieldNames?: (keyof ROW)[]
114
114
  _groupByFieldNames?: (keyof ROW)[]
115
115
  _distinct = false
116
- _ids?: ROW['id'][]
117
116
 
118
117
  filter(name: keyof ROW, op: DBQueryFilterOperator, val: any): this {
119
118
  this._filters.push({ name, op, val })
@@ -125,6 +124,11 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
125
124
  return this
126
125
  }
127
126
 
127
+ filterIn(name: keyof ROW, val: any[]): this {
128
+ this._filters.push({ name, op: 'in', val })
129
+ return this
130
+ }
131
+
128
132
  limit(limit: number): this {
129
133
  this._limitValue = limit
130
134
  return this
@@ -168,28 +172,6 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
168
172
  return this
169
173
  }
170
174
 
171
- /**
172
- * Allows to query by ids (one or many).
173
- * Similar to:
174
- * SELECT * FROM table where id in (a, b, c)
175
- * or (if only 1 id is passed)
176
- * SELECT * FROM table where id = a
177
- */
178
- byIds(ids: ROW['id'][]): this {
179
- this._ids = ids
180
- return this
181
- }
182
-
183
- /**
184
- * Allows to query by id.
185
- * Similar to:
186
- * SELECT * FROM table where id = a
187
- */
188
- byId(id: ROW['id']): this {
189
- this._ids = [id]
190
- return this
191
- }
192
-
193
175
  clone(): DBQuery<ROW> {
194
176
  return _objectAssign(new DBQuery<ROW>(this.table), {
195
177
  _filters: [...this._filters],
@@ -201,7 +183,6 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
201
183
  _distinct: this._distinct,
202
184
  _startCursor: this._startCursor,
203
185
  _endCursor: this._endCursor,
204
- _ids: this._ids,
205
186
  })
206
187
  }
207
188
 
@@ -222,14 +203,6 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
222
203
  )
223
204
  }
224
205
 
225
- if (this._ids?.length) {
226
- if (this._ids.length === 1) {
227
- tokens.push(`id=${this._ids[0]}`)
228
- } else {
229
- tokens.push(`ids in (${this._ids.join(',')})`)
230
- }
231
- }
232
-
233
206
  tokens.push(
234
207
  ...this._filters.map(f => `${f.name as string}${f.op}${f.val}`),
235
208
  ...this._orders.map(o => `order by ${o.name as string}${o.descending ? ' desc' : ''}`),
@@ -1,6 +1,6 @@
1
1
  import { pDelay, _deepCopy, _pick, _sortBy, _omit, localTime } from '@naturalcycles/js-lib'
2
2
  import { readableToArray, transformNoOp } from '@naturalcycles/nodejs-lib'
3
- import { CommonDaoLogLevel } from '..'
3
+ import { CommonDaoLogLevel, DBQuery } from '..'
4
4
  import { CommonDB } from '../common.db'
5
5
  import { CommonDao } from '../commondao/common.dao'
6
6
  import { CommonDBImplementationFeatures, CommonDBImplementationQuirks, expectMatch } from './dbTest'
@@ -75,9 +75,12 @@ export function runCommonDaoTest(
75
75
  // DELETE ALL initially
76
76
  test('deleteByIds test items', async () => {
77
77
  const rows = await dao.query().select(['id']).runQuery()
78
- await db.deleteByIds(
79
- TEST_TABLE,
80
- rows.map(i => i.id),
78
+ await db.deleteByQuery(
79
+ DBQuery.create(TEST_TABLE).filter(
80
+ 'id',
81
+ 'in',
82
+ rows.map(r => r.id),
83
+ ),
81
84
  )
82
85
  })
83
86
 
@@ -156,7 +159,11 @@ export function runCommonDaoTest(
156
159
  // GET not empty
157
160
  test('getByIds all items', async () => {
158
161
  const rows = await dao.getByIds(items.map(i => i.id).concat('abcd'))
159
- expectMatch(expectedItems, rows, quirks)
162
+ expectMatch(
163
+ expectedItems,
164
+ _sortBy(rows, r => r.id),
165
+ quirks,
166
+ )
160
167
  })
161
168
 
162
169
  // QUERY
@@ -258,11 +265,7 @@ export function runCommonDaoTest(
258
265
 
259
266
  test('cleanup', async () => {
260
267
  // CLEAN UP
261
- const rows = await dao.query().select(['id']).runQuery()
262
- await db.deleteByIds(
263
- TEST_TABLE,
264
- rows.map(i => i.id),
265
- )
268
+ await dao.query().deleteByQuery()
266
269
  })
267
270
  }
268
271
 
@@ -129,9 +129,11 @@ export function runCommonDBTest(
129
129
  // DELETE ALL initially
130
130
  test('deleteByIds test items', async () => {
131
131
  const { rows } = await db.runQuery(queryAll().select(['id']))
132
- await db.deleteByIds(
133
- TEST_TABLE,
134
- rows.map(i => i.id),
132
+ await db.deleteByQuery(
133
+ queryAll().filterIn(
134
+ 'id',
135
+ rows.map(i => i.id),
136
+ ),
135
137
  )
136
138
  })
137
139
 
@@ -145,17 +147,17 @@ export function runCommonDBTest(
145
147
 
146
148
  // GET empty
147
149
  test('getByIds(item1.id) should return empty', async () => {
148
- const [item1Loaded] = await db.getByIds<TestItemDBM>(TEST_TABLE, [item1.id])
150
+ const [item1Loaded] = (await db.runQuery(queryAll().filterEq('id', item1.id))).rows
149
151
  // console.log(a)
150
152
  expect(item1Loaded).toBeUndefined()
151
153
  })
152
154
 
153
155
  test('getByIds([]) should return []', async () => {
154
- expect(await db.getByIds(TEST_TABLE, [])).toEqual([])
156
+ expect((await db.runQuery(queryAll().filterIn('id', []))).rows).toEqual([])
155
157
  })
156
158
 
157
159
  test('getByIds(...) should return empty', async () => {
158
- expect(await db.getByIds(TEST_TABLE, ['abc', 'abcd'])).toEqual([])
160
+ expect((await db.runQuery(queryAll().filterIn('id', ['abc', 'abcd']))).rows).toEqual([])
159
161
  })
160
162
 
161
163
  // SAVE
@@ -167,7 +169,7 @@ export function runCommonDBTest(
167
169
  }
168
170
  deepFreeze(item3)
169
171
  await db.saveBatch(TEST_TABLE, [item3])
170
- const item3Loaded = (await db.getByIds<TestItemDBM>(TEST_TABLE, [item3.id]))[0]!
172
+ const item3Loaded = (await db.runQuery(queryAll().filterEq('id', item3.id))).rows[0]!
171
173
  expectMatch([item3], [item3Loaded], quirks)
172
174
  expect(item3Loaded.k2).toBeNull()
173
175
  })
@@ -184,7 +186,7 @@ export function runCommonDBTest(
184
186
  delete expected.k2
185
187
 
186
188
  await db.saveBatch(TEST_TABLE, [item3])
187
- const item3Loaded = (await db.getByIds<TestItemDBM>(TEST_TABLE, [item3.id]))[0]!
189
+ const item3Loaded = (await db.runQuery(queryAll().filterEq('id', item3.id))).rows[0]!
188
190
  expectMatch([expected], [item3Loaded], quirks)
189
191
  expect(item3Loaded.k2).toBeUndefined()
190
192
  expect(Object.keys(item3Loaded)).not.toContain('k2')
@@ -219,8 +221,13 @@ export function runCommonDBTest(
219
221
 
220
222
  // GET not empty
221
223
  test('getByIds all items', async () => {
222
- const rows = await db.getByIds<TestItemDBM>(TEST_TABLE, items.map(i => i.id).concat('abcd'))
223
- expectMatch(items, rows, quirks)
224
+ const rows = (await db.runQuery(queryAll().filterIn('id', items.map(i => i.id).concat('abcd'))))
225
+ .rows
226
+ expectMatch(
227
+ items,
228
+ _sortBy(rows, r => r.id),
229
+ quirks,
230
+ )
224
231
  })
225
232
 
226
233
  // QUERY
@@ -340,8 +347,8 @@ export function runCommonDBTest(
340
347
  b1,
341
348
  }
342
349
  await db.saveBatch(TEST_TABLE, [item])
343
- const [loaded] = await db.getByIds<TestItemDBM>(TEST_TABLE, [item.id])
344
- const b1Loaded = loaded!.b1!
350
+ const loaded = (await db.runQuery(queryAll().filterEq('id', item.id))).rows[0]!
351
+ const b1Loaded = loaded.b1!
345
352
  // console.log({
346
353
  // b11: typeof b1,
347
354
  // b12: typeof b1Loaded,
@@ -391,7 +398,6 @@ export function runCommonDBTest(
391
398
  }
392
399
 
393
400
  if (updateByQuery) {
394
- // todo: query by ids (same as getByIds)
395
401
  test('updateByQuery simple', async () => {
396
402
  // cleanup, reset initial data
397
403
  await db.deleteByQuery(queryAll())
@@ -445,11 +451,7 @@ export function runCommonDBTest(
445
451
  if (querying) {
446
452
  test('cleanup', async () => {
447
453
  // CLEAN UP
448
- const { rows } = await db.runQuery(queryAll().select(['id']))
449
- await db.deleteByIds(
450
- TEST_TABLE,
451
- rows.map(i => i.id),
452
- )
454
+ await db.deleteByQuery(queryAll())
453
455
  })
454
456
  }
455
457
  }
@@ -1,5 +1,6 @@
1
1
  import type { CommonDB } from '../common.db'
2
2
  import { CommonDBSaveOptions, DBOperation } from '../db.model'
3
+ import { DBQuery } from '../query/dbQuery'
3
4
  import { DBTransaction } from './dbTransaction'
4
5
 
5
6
  /**
@@ -80,7 +81,10 @@ export async function commitDBTransactionSimple(
80
81
  if (op.type === 'saveBatch') {
81
82
  await db.saveBatch(op.table, op.rows, { ...op.opt, ...opt })
82
83
  } else if (op.type === 'deleteByIds') {
83
- await db.deleteByIds(op.table, op.ids, { ...op.opt, ...opt })
84
+ await db.deleteByQuery(DBQuery.create(op.table).filter('id', 'in', op.ids), {
85
+ ...op.opt,
86
+ ...opt,
87
+ })
84
88
  } else {
85
89
  throw new Error(`DBOperation not supported: ${(op as any).type}`)
86
90
  }