@naturalcycles/db-lib 9.21.0 → 9.22.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.
Files changed (35) hide show
  1. package/dist/adapter/cachedb/cache.db.d.ts +2 -2
  2. package/dist/adapter/cachedb/cache.db.js +4 -3
  3. package/dist/adapter/file/file.db.js +2 -1
  4. package/dist/adapter/inmemory/inMemory.db.d.ts +7 -6
  5. package/dist/adapter/inmemory/inMemory.db.js +15 -13
  6. package/dist/adapter/inmemory/inMemoryKeyValueDB.d.ts +1 -0
  7. package/dist/adapter/inmemory/inMemoryKeyValueDB.js +13 -0
  8. package/dist/base.common.db.d.ts +4 -3
  9. package/dist/base.common.db.js +5 -2
  10. package/dist/common.db.d.ts +23 -14
  11. package/dist/common.db.js +2 -2
  12. package/dist/commondao/common.dao.d.ts +16 -5
  13. package/dist/commondao/common.dao.js +37 -8
  14. package/dist/db.model.d.ts +0 -13
  15. package/dist/db.model.js +1 -17
  16. package/dist/kv/commonKeyValueDB.d.ts +16 -2
  17. package/dist/query/dbQuery.d.ts +2 -2
  18. package/dist/query/dbQuery.js +2 -2
  19. package/dist/testing/daoTest.js +26 -0
  20. package/dist/testing/dbTest.js +21 -23
  21. package/dist/testing/keyValueDBTest.js +15 -3
  22. package/package.json +1 -1
  23. package/src/adapter/cachedb/cache.db.ts +6 -5
  24. package/src/adapter/file/file.db.ts +2 -1
  25. package/src/adapter/inmemory/inMemory.db.ts +29 -18
  26. package/src/adapter/inmemory/inMemoryKeyValueDB.ts +17 -1
  27. package/src/base.common.db.ts +18 -5
  28. package/src/common.db.ts +36 -17
  29. package/src/commondao/common.dao.ts +46 -11
  30. package/src/db.model.ts +0 -19
  31. package/src/kv/commonKeyValueDB.ts +17 -2
  32. package/src/query/dbQuery.ts +2 -3
  33. package/src/testing/daoTest.ts +28 -0
  34. package/src/testing/dbTest.ts +22 -28
  35. package/src/testing/keyValueDBTest.ts +17 -3
@@ -2,7 +2,7 @@ import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcy
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
3
  import { BaseCommonDB } from '../../base.common.db';
4
4
  import { CommonDB, CommonDBSupport } from '../../common.db';
5
- import { DBPatch, RunQueryResult } from '../../db.model';
5
+ import { RunQueryResult } from '../../db.model';
6
6
  import { DBQuery } from '../../query/dbQuery';
7
7
  import { CacheDBCfg, CacheDBCreateOptions, CacheDBOptions, CacheDBSaveOptions, CacheDBStreamOptions } from './cache.db.model';
8
8
  /**
@@ -28,5 +28,5 @@ export declare class CacheDB extends BaseCommonDB implements CommonDB {
28
28
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBOptions): Promise<number>;
29
29
  streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBStreamOptions): ReadableTyped<ROW>;
30
30
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CacheDBOptions): Promise<number>;
31
- updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>, opt?: CacheDBOptions): Promise<number>;
31
+ patchByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: Partial<ROW>, opt?: CacheDBOptions): Promise<number>;
32
32
  }
@@ -17,6 +17,7 @@ class CacheDB extends base_common_db_1.BaseCommonDB {
17
17
  this.support = {
18
18
  ...common_db_1.commonDBFullSupport,
19
19
  transactions: false,
20
+ increment: false,
20
21
  };
21
22
  this.cfg = {
22
23
  logger: console,
@@ -180,13 +181,13 @@ class CacheDB extends base_common_db_1.BaseCommonDB {
180
181
  }
181
182
  return deletedIds;
182
183
  }
183
- async updateByQuery(q, patch, opt = {}) {
184
+ async patchByQuery(q, patch, opt = {}) {
184
185
  let updated;
185
186
  if (!opt.onlyCache && !this.cfg.onlyCache) {
186
- updated = await this.cfg.downstreamDB.updateByQuery(q, patch, opt);
187
+ updated = await this.cfg.downstreamDB.patchByQuery(q, patch, opt);
187
188
  }
188
189
  if (!opt.skipCache && !this.cfg.skipCache) {
189
- const cacheResult = this.cfg.cacheDB.updateByQuery(q, patch, opt);
190
+ const cacheResult = this.cfg.cacheDB.patchByQuery(q, patch, opt);
190
191
  if (this.cfg.awaitCache)
191
192
  updated ??= await cacheResult;
192
193
  }
@@ -22,9 +22,10 @@ class FileDB extends __1.BaseCommonDB {
22
22
  bufferValues: false, // todo: implement
23
23
  insertSaveMethod: false,
24
24
  updateSaveMethod: false,
25
- updateByQuery: false,
25
+ patchByQuery: false,
26
26
  createTable: false,
27
27
  transactions: false, // todo
28
+ increment: false,
28
29
  };
29
30
  this.cfg = {
30
31
  sortObjects: true,
@@ -1,6 +1,6 @@
1
- import { CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, StringMap } from '@naturalcycles/js-lib';
1
+ import { AnyObjectWithId, CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, StringMap } from '@naturalcycles/js-lib';
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
- import { CommonDB, CommonDBTransactionOptions, CommonDBType, DBOperation, DBPatch, DBTransactionFn } from '../..';
3
+ import { CommonDB, CommonDBTransactionOptions, CommonDBType, DBOperation, DBTransactionFn } from '../..';
4
4
  import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, DBTransaction, RunQueryResult } from '../../db.model';
5
5
  import { DBQuery } from '../../query/dbQuery';
6
6
  export interface InMemoryDBCfg {
@@ -55,8 +55,8 @@ export declare class InMemoryDB implements CommonDB {
55
55
  dbQuerySelectFields?: boolean;
56
56
  insertSaveMethod?: boolean;
57
57
  updateSaveMethod?: boolean;
58
- updateByQuery?: boolean;
59
- dbIncrement?: boolean;
58
+ patchByQuery?: boolean;
59
+ increment?: boolean;
60
60
  createTable?: boolean;
61
61
  tableSchemas?: boolean;
62
62
  streaming?: boolean;
@@ -66,7 +66,7 @@ export declare class InMemoryDB implements CommonDB {
66
66
  };
67
67
  constructor(cfg?: Partial<InMemoryDBCfg>);
68
68
  cfg: InMemoryDBCfg;
69
- data: StringMap<StringMap<ObjectWithId>>;
69
+ data: StringMap<StringMap<AnyObjectWithId>>;
70
70
  /**
71
71
  * Returns internal "Data snapshot".
72
72
  * Deterministic - jsonSorted.
@@ -84,11 +84,12 @@ export declare class InMemoryDB implements CommonDB {
84
84
  saveBatch<ROW extends ObjectWithId>(_table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
85
85
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
86
86
  deleteByIds(_table: string, ids: string[], _opt?: CommonDBOptions): Promise<number>;
87
- updateByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>): Promise<number>;
87
+ patchByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: Partial<ROW>): Promise<number>;
88
88
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<RunQueryResult<ROW>>;
89
89
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): Promise<number>;
90
90
  streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBOptions): ReadableTyped<ROW>;
91
91
  runInTransaction(fn: DBTransactionFn, opt?: CommonDBTransactionOptions): Promise<void>;
92
+ incrementBatch(table: string, prop: string, incrementMap: StringMap<number>, _opt?: CommonDBOptions): Promise<StringMap<number>>;
92
93
  /**
93
94
  * Flushes all tables (all namespaces) at once.
94
95
  */
@@ -113,22 +113,12 @@ class InMemoryDB {
113
113
  });
114
114
  return count;
115
115
  }
116
- async updateByQuery(q, patch) {
117
- const patchEntries = Object.entries(patch);
118
- if (!patchEntries.length)
116
+ async patchByQuery(q, patch) {
117
+ if ((0, js_lib_1._isEmptyObject)(patch))
119
118
  return 0;
120
119
  const table = this.cfg.tablesPrefix + q.table;
121
120
  const rows = (0, __1.queryInMemory)(q, Object.values(this.data[table] || {}));
122
- rows.forEach((row) => {
123
- patchEntries.forEach(([k, v]) => {
124
- if (v instanceof __1.DBIncrement) {
125
- row[k] = (row[k] || 0) + v.amount;
126
- }
127
- else {
128
- row[k] = v;
129
- }
130
- });
131
- });
121
+ rows.forEach(row => Object.assign(row, patch));
132
122
  return rows.length;
133
123
  }
134
124
  async runQuery(q, _opt) {
@@ -157,6 +147,18 @@ class InMemoryDB {
157
147
  throw err;
158
148
  }
159
149
  }
150
+ async incrementBatch(table, prop, incrementMap, _opt) {
151
+ const tbl = this.cfg.tablesPrefix + table;
152
+ this.data[tbl] ||= {};
153
+ const result = {};
154
+ for (const [id, by] of (0, js_lib_1._stringMapEntries)(incrementMap)) {
155
+ this.data[tbl][id] ||= { id };
156
+ const newValue = (this.data[tbl][id][prop] || 0) + by;
157
+ this.data[tbl][id][prop] = newValue;
158
+ result[id] = newValue;
159
+ }
160
+ return result;
161
+ }
160
162
  /**
161
163
  * Flushes all tables (all namespaces) at once.
162
164
  */
@@ -22,4 +22,5 @@ export declare class InMemoryKeyValueDB implements CommonKeyValueDB {
22
22
  streamEntries(table: string, limit?: number): ReadableTyped<KeyValueDBTuple>;
23
23
  count(table: string): Promise<number>;
24
24
  increment(table: string, id: string, by?: number): Promise<number>;
25
+ incrementBatch(table: string, incrementMap: StringMap<number>): Promise<StringMap<number>>;
25
26
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InMemoryKeyValueDB = void 0;
4
4
  const node_stream_1 = require("node:stream");
5
+ const js_lib_1 = require("@naturalcycles/js-lib");
5
6
  const commonKeyValueDB_1 = require("../../kv/commonKeyValueDB");
6
7
  class InMemoryKeyValueDB {
7
8
  constructor(cfg = {}) {
@@ -18,6 +19,7 @@ class InMemoryKeyValueDB {
18
19
  this.data[table] ||= {};
19
20
  ids.forEach(id => delete this.data[table][id]);
20
21
  }
22
+ // todo: but should we work with Tuples or Objects?
21
23
  async getByIds(table, ids) {
22
24
  this.data[table] ||= {};
23
25
  return ids.map(id => [id, this.data[table][id]]).filter(e => e[1]);
@@ -46,5 +48,16 @@ class InMemoryKeyValueDB {
46
48
  this.data[table][id] = Buffer.from(String(newValue));
47
49
  return newValue;
48
50
  }
51
+ async incrementBatch(table, incrementMap) {
52
+ this.data[table] ||= {};
53
+ const result = {};
54
+ for (const [id, by] of (0, js_lib_1._stringMapEntries)(incrementMap)) {
55
+ const newValue = parseInt(this.data[table][id]?.toString() || '0') + by;
56
+ // todo: but should this.data store Buffer or number for incremented values?
57
+ this.data[table][id] = Buffer.from(String(newValue));
58
+ result[id] = newValue;
59
+ }
60
+ return result;
61
+ }
49
62
  }
50
63
  exports.InMemoryKeyValueDB = InMemoryKeyValueDB;
@@ -1,7 +1,7 @@
1
- import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
1
+ import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, StringMap } from '@naturalcycles/js-lib';
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
3
  import { CommonDB, CommonDBSupport, CommonDBType } from './common.db';
4
- import { CommonDBOptions, CommonDBSaveOptions, CommonDBTransactionOptions, DBPatch, DBTransactionFn, RunQueryResult } from './db.model';
4
+ import { CommonDBOptions, CommonDBSaveOptions, CommonDBTransactionOptions, DBTransactionFn, RunQueryResult } from './db.model';
5
5
  import { DBQuery } from './query/dbQuery';
6
6
  /**
7
7
  * No-op implementation of CommonDB interface.
@@ -16,11 +16,12 @@ export declare class BaseCommonDB implements CommonDB {
16
16
  createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>): Promise<void>;
17
17
  getByIds<ROW extends ObjectWithId>(_table: string, _ids: string[]): Promise<ROW[]>;
18
18
  deleteByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
19
- updateByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>, _patch: DBPatch<ROW>, _opt?: CommonDBOptions): Promise<number>;
19
+ patchByQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>, _patch: Partial<ROW>, _opt?: CommonDBOptions): Promise<number>;
20
20
  runQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<RunQueryResult<ROW>>;
21
21
  runQueryCount<ROW extends ObjectWithId>(_q: DBQuery<ROW>): Promise<number>;
22
22
  saveBatch<ROW extends ObjectWithId>(_table: string, _rows: ROW[], _opt?: CommonDBSaveOptions<ROW>): Promise<void>;
23
23
  streamQuery<ROW extends ObjectWithId>(_q: DBQuery<ROW>): ReadableTyped<ROW>;
24
24
  deleteByIds(_table: string, _ids: string[], _opt?: CommonDBOptions): Promise<number>;
25
25
  runInTransaction(fn: DBTransactionFn, _opt?: CommonDBTransactionOptions): Promise<void>;
26
+ incrementBatch(_table: string, _prop: string, _incrementMap: StringMap<number>, _opt?: CommonDBOptions): Promise<StringMap<number>>;
26
27
  }
@@ -30,8 +30,8 @@ class BaseCommonDB {
30
30
  async deleteByQuery(_q) {
31
31
  throw new Error('deleteByQuery is not implemented');
32
32
  }
33
- async updateByQuery(_q, _patch, _opt) {
34
- throw new Error('updateByQuery is not implemented');
33
+ async patchByQuery(_q, _patch, _opt) {
34
+ throw new Error('patchByQuery is not implemented');
35
35
  }
36
36
  async runQuery(_q) {
37
37
  throw new Error('runQuery is not implemented');
@@ -53,5 +53,8 @@ class BaseCommonDB {
53
53
  await fn(tx);
54
54
  // there's no try/catch and rollback, as there's nothing to rollback
55
55
  }
56
+ async incrementBatch(_table, _prop, _incrementMap, _opt) {
57
+ throw new Error('incrementBatch is not implemented');
58
+ }
56
59
  }
57
60
  exports.BaseCommonDB = BaseCommonDB;
@@ -1,6 +1,6 @@
1
- import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
1
+ import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, StringMap } from '@naturalcycles/js-lib';
2
2
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
- import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, CommonDBTransactionOptions, DBPatch, DBTransactionFn, RunQueryResult } from './db.model';
3
+ import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, CommonDBTransactionOptions, DBTransactionFn, RunQueryResult } from './db.model';
4
4
  import { DBQuery } from './query/dbQuery';
5
5
  export declare enum CommonDBType {
6
6
  'document' = "document",
@@ -64,7 +64,7 @@ export interface CommonDB {
64
64
  */
65
65
  deleteByQuery: <ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: CommonDBOptions) => Promise<number>;
66
66
  /**
67
- * Applies patch to the rows returned by the query.
67
+ * Applies patch to all the rows that are matched by the query.
68
68
  *
69
69
  * Example:
70
70
  *
@@ -72,16 +72,9 @@ export interface CommonDB {
72
72
  *
73
73
  * patch would be { A: 'B' } for that query.
74
74
  *
75
- * Supports "increment query", example:
76
- *
77
- * UPDATE table SET A = A + 1
78
- *
79
- * In that case patch would look like:
80
- * { A: DBIncrement(1) }
81
- *
82
- * Returns number of rows affected.
75
+ * Returns the number of rows affected.
83
76
  */
84
- updateByQuery: <ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: DBPatch<ROW>, opt?: CommonDBOptions) => Promise<number>;
77
+ patchByQuery: <ROW extends ObjectWithId>(q: DBQuery<ROW>, patch: Partial<ROW>, opt?: CommonDBOptions) => Promise<number>;
85
78
  /**
86
79
  * Should be implemented as a Transaction (best effort), which means that
87
80
  * either ALL or NONE of the operations should be applied.
@@ -94,6 +87,22 @@ export interface CommonDB {
94
87
  * unless specified as readOnly in CommonDBTransactionOptions.
95
88
  */
96
89
  runInTransaction: (fn: DBTransactionFn, opt?: CommonDBTransactionOptions) => Promise<void>;
90
+ /**
91
+ * Increments a value of a property by a given amount.
92
+ * This is a batch operation, so it allows to increment multiple rows at once.
93
+ *
94
+ * - table - the table to apply operations on
95
+ * - prop - name of the property to increment (in each of the rows passed)
96
+ * - incrementMap - map from id to increment value
97
+ *
98
+ * Example of incrementMap:
99
+ * { rowId1: 2, rowId2: 3 }
100
+ *
101
+ * Returns the incrementMap with the same keys and updated values.
102
+ *
103
+ * @experimental
104
+ */
105
+ incrementBatch: (table: string, prop: string, incrementMap: StringMap<number>, opt?: CommonDBOptions) => Promise<StringMap<number>>;
97
106
  }
98
107
  /**
99
108
  * Manifest of supported features.
@@ -106,8 +115,8 @@ export interface CommonDBSupport {
106
115
  dbQuerySelectFields?: boolean;
107
116
  insertSaveMethod?: boolean;
108
117
  updateSaveMethod?: boolean;
109
- updateByQuery?: boolean;
110
- dbIncrement?: boolean;
118
+ patchByQuery?: boolean;
119
+ increment?: boolean;
111
120
  createTable?: boolean;
112
121
  tableSchemas?: boolean;
113
122
  streaming?: boolean;
package/dist/common.db.js CHANGED
@@ -14,8 +14,8 @@ exports.commonDBFullSupport = {
14
14
  dbQuerySelectFields: true,
15
15
  insertSaveMethod: true,
16
16
  updateSaveMethod: true,
17
- updateByQuery: true,
18
- dbIncrement: true,
17
+ patchByQuery: true,
18
+ increment: true,
19
19
  createTable: true,
20
20
  tableSchemas: true,
21
21
  streaming: true,
@@ -1,7 +1,7 @@
1
1
  import { Transform } from 'node:stream';
2
- import { AsyncMapper, BaseDBEntity, CommonLogger, JsonSchemaObject, JsonSchemaRootObject, UnixTimestampMillisNumber, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
2
+ import { AsyncMapper, BaseDBEntity, CommonLogger, JsonSchemaObject, JsonSchemaRootObject, StringMap, UnixTimestampMillisNumber, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
3
3
  import { AjvSchema, ObjectSchema, ReadableTyped } from '@naturalcycles/nodejs-lib';
4
- import { CommonDBTransactionOptions, DBPatch, DBTransaction, RunQueryResult } from '../db.model';
4
+ import { CommonDBTransactionOptions, DBTransaction, RunQueryResult } from '../db.model';
5
5
  import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
6
6
  import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoPatchByIdOptions, CommonDaoPatchOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions } from './common.dao.model';
7
7
  /**
@@ -125,9 +125,20 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
125
125
  * This is expected to be more memory-efficient way of deleting large number of rows.
126
126
  */
127
127
  deleteByQuery(q: DBQuery<DBM>, opt?: CommonDaoStreamDeleteOptions<DBM>): Promise<number>;
128
- updateById(id: ID, patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
129
- updateByIds(ids: ID[], patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
130
- updateByQuery(q: DBQuery<DBM>, patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
128
+ patchByIds(ids: ID[], patch: Partial<DBM>, opt?: CommonDaoOptions): Promise<number>;
129
+ patchByQuery(q: DBQuery<DBM>, patch: Partial<DBM>, opt?: CommonDaoOptions): Promise<number>;
130
+ /**
131
+ * Caveat: it doesn't update created/updated props.
132
+ *
133
+ * @experimental
134
+ */
135
+ increment(prop: keyof DBM, id: ID, by?: number, opt?: CommonDaoOptions): Promise<number>;
136
+ /**
137
+ * Caveat: it doesn't update created/updated props.
138
+ *
139
+ * @experimental
140
+ */
141
+ incrementBatch(prop: keyof DBM, incrementMap: StringMap<number>, opt?: CommonDaoOptions): Promise<StringMap<number>>;
131
142
  dbmToBM(_dbm: undefined, opt?: CommonDaoOptions): Promise<undefined>;
132
143
  dbmToBM(_dbm?: DBM, opt?: CommonDaoOptions): Promise<BM>;
133
144
  dbmsToBM(dbms: DBM[], opt?: CommonDaoOptions): Promise<BM[]>;
@@ -829,25 +829,54 @@ class CommonDao {
829
829
  this.logSaveResult(started, op, q.table);
830
830
  return deleted;
831
831
  }
832
- async updateById(id, patch, opt = {}) {
833
- return await this.updateByQuery(this.query().filterEq('id', id), patch, opt);
834
- }
835
- async updateByIds(ids, patch, opt = {}) {
832
+ async patchByIds(ids, patch, opt = {}) {
836
833
  if (!ids.length)
837
834
  return 0;
838
- return await this.updateByQuery(this.query().filterIn('id', ids), patch, opt);
835
+ return await this.patchByQuery(this.query().filterIn('id', ids), patch, opt);
839
836
  }
840
- async updateByQuery(q, patch, opt = {}) {
837
+ async patchByQuery(q, patch, opt = {}) {
841
838
  this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
842
839
  this.requireWriteAccess();
843
840
  this.requireObjectMutability(opt);
844
841
  q.table = opt.table || q.table;
845
- const op = `updateByQuery(${q.pretty()})`;
842
+ const op = `patchByQuery(${q.pretty()})`;
846
843
  const started = this.logStarted(op, q.table);
847
- const updated = await this.cfg.db.updateByQuery(q, patch, opt);
844
+ const updated = await this.cfg.db.patchByQuery(q, patch, opt);
848
845
  this.logSaveResult(started, op, q.table);
849
846
  return updated;
850
847
  }
848
+ /**
849
+ * Caveat: it doesn't update created/updated props.
850
+ *
851
+ * @experimental
852
+ */
853
+ async increment(prop, id, by = 1, opt = {}) {
854
+ this.requireWriteAccess();
855
+ this.requireObjectMutability(opt);
856
+ const { table } = this.cfg;
857
+ const op = `increment`;
858
+ const started = this.logStarted(op, table);
859
+ const result = await this.cfg.db.incrementBatch(table, prop, {
860
+ [id]: by,
861
+ });
862
+ this.logSaveResult(started, op, table);
863
+ return result[id];
864
+ }
865
+ /**
866
+ * Caveat: it doesn't update created/updated props.
867
+ *
868
+ * @experimental
869
+ */
870
+ async incrementBatch(prop, incrementMap, opt = {}) {
871
+ this.requireWriteAccess();
872
+ this.requireObjectMutability(opt);
873
+ const { table } = this.cfg;
874
+ const op = `incrementBatch`;
875
+ const started = this.logStarted(op, table);
876
+ const result = await this.cfg.db.incrementBatch(table, prop, incrementMap);
877
+ this.logSaveResult(started, op, table);
878
+ return result;
879
+ }
851
880
  async dbmToBM(_dbm, opt = {}) {
852
881
  if (!_dbm)
853
882
  return;
@@ -96,16 +96,3 @@ export declare enum DBModelType {
96
96
  DBM = "DBM",
97
97
  BM = "BM"
98
98
  }
99
- /**
100
- * Allows to construct a query similar to:
101
- *
102
- * UPDATE table SET A = A + 1
103
- *
104
- * In this case DBIncement.of(1) will be needed.
105
- */
106
- export declare class DBIncrement {
107
- amount: number;
108
- private constructor();
109
- static of(amount: number): DBIncrement;
110
- }
111
- export type DBPatch<ROW extends ObjectWithId> = Partial<Record<keyof ROW, ROW[keyof ROW] | DBIncrement>>;
package/dist/db.model.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DBIncrement = exports.DBModelType = exports.DBRelation = void 0;
3
+ exports.DBModelType = exports.DBRelation = void 0;
4
4
  var DBRelation;
5
5
  (function (DBRelation) {
6
6
  DBRelation["ONE_TO_ONE"] = "ONE_TO_ONE";
@@ -11,19 +11,3 @@ var DBModelType;
11
11
  DBModelType["DBM"] = "DBM";
12
12
  DBModelType["BM"] = "BM";
13
13
  })(DBModelType || (exports.DBModelType = DBModelType = {}));
14
- /**
15
- * Allows to construct a query similar to:
16
- *
17
- * UPDATE table SET A = A + 1
18
- *
19
- * In this case DBIncement.of(1) will be needed.
20
- */
21
- class DBIncrement {
22
- constructor(amount) {
23
- this.amount = amount;
24
- }
25
- static of(amount) {
26
- return new DBIncrement(amount);
27
- }
28
- }
29
- exports.DBIncrement = DBIncrement;
@@ -1,4 +1,4 @@
1
- import { UnixTimestampNumber } from '@naturalcycles/js-lib';
1
+ import { StringMap, UnixTimestampNumber } from '@naturalcycles/js-lib';
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
3
  import { CommonDBCreateOptions } from '../db.model';
4
4
  /**
@@ -33,13 +33,27 @@ export interface CommonKeyValueDB {
33
33
  streamEntries: (table: string, limit?: number) => ReadableTyped<KeyValueDBTuple>;
34
34
  count: (table: string) => Promise<number>;
35
35
  /**
36
- *
37
36
  * Increments the value of a key in a table by a given amount.
38
37
  * Default increment is 1 when `by` is not provided.
39
38
  *
40
39
  * Returns the new value.
40
+ *
41
+ * @experimental
41
42
  */
42
43
  increment: (table: string, id: string, by?: number) => Promise<number>;
44
+ /**
45
+ * Perform a batch of Increment operations.
46
+ * Given incrementMap, increment each key of it by the given amount (value of the map).
47
+ *
48
+ * Example:
49
+ * { key1: 2, key2: 3 }
50
+ * would increment `key1` by 2, and `key2` by 3.
51
+ *
52
+ * Returns the incrementMap with the same keys and updated values.
53
+ *
54
+ * @experimental
55
+ */
56
+ incrementBatch: (table: string, incrementMap: StringMap<number>) => Promise<StringMap<number>>;
43
57
  }
44
58
  export type KeyValueDBTuple = [key: string, value: Buffer];
45
59
  export interface CommonKeyValueDBSaveBatchOptions {
@@ -1,6 +1,6 @@
1
1
  import { AsyncMapper, BaseDBEntity, ObjectWithId } from '@naturalcycles/js-lib';
2
2
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
- import { CommonDaoOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, DBPatch } from '..';
3
+ import { CommonDaoOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions } from '..';
4
4
  import { CommonDao } from '../commondao/common.dao';
5
5
  import { RunQueryResult } from '../db.model';
6
6
  /**
@@ -99,7 +99,7 @@ export declare class RunnableDBQuery<BM extends BaseDBEntity, DBM extends BaseDB
99
99
  runQueryExtended(opt?: CommonDaoOptions): Promise<RunQueryResult<BM>>;
100
100
  runQueryExtendedAsDBM(opt?: CommonDaoOptions): Promise<RunQueryResult<DBM>>;
101
101
  runQueryCount(opt?: CommonDaoOptions): Promise<number>;
102
- updateByQuery(patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
102
+ patchByQuery(patch: Partial<DBM>, opt?: CommonDaoOptions): Promise<number>;
103
103
  streamQueryForEach(mapper: AsyncMapper<BM, void>, opt?: CommonDaoStreamForEachOptions<BM>): Promise<void>;
104
104
  streamQueryAsDBMForEach(mapper: AsyncMapper<DBM, void>, opt?: CommonDaoStreamForEachOptions<DBM>): Promise<void>;
105
105
  streamQuery(opt?: CommonDaoStreamOptions<BM>): ReadableTyped<BM>;
@@ -165,8 +165,8 @@ class RunnableDBQuery extends DBQuery {
165
165
  async runQueryCount(opt) {
166
166
  return await this.dao.runQueryCount(this, opt);
167
167
  }
168
- async updateByQuery(patch, opt) {
169
- return await this.dao.updateByQuery(this, patch, opt);
168
+ async patchByQuery(patch, opt) {
169
+ return await this.dao.patchByQuery(this, patch, opt);
170
170
  }
171
171
  async streamQueryForEach(mapper, opt) {
172
172
  await this.dao.streamQueryForEach(this, mapper, opt);
@@ -99,6 +99,32 @@ function runCommonDaoTest(db, quirks = {}) {
99
99
  expect(items[0]).toMatchObject(clone);
100
100
  (0, dbTest_1.expectMatch)(expectedItems, itemsSaved, quirks);
101
101
  });
102
+ if (support.increment) {
103
+ test('increment', async () => {
104
+ await dao.incrementBatch('k3', { id1: 1, id2: 2 });
105
+ let rows = await dao.query().runQuery();
106
+ rows = (0, js_lib_1._sortBy)(rows, r => r.id);
107
+ const expected = expectedItems.map(r => {
108
+ if (r.id === 'id1') {
109
+ return {
110
+ ...r,
111
+ k3: r.k3 + 1,
112
+ };
113
+ }
114
+ if (r.id === 'id2') {
115
+ return {
116
+ ...r,
117
+ k3: r.k3 + 2,
118
+ };
119
+ }
120
+ return r;
121
+ });
122
+ (0, dbTest_1.expectMatch)(expected, rows, quirks);
123
+ // reset the changes
124
+ await dao.increment('k3', 'id1', -1);
125
+ await dao.increment('k3', 'id2', -2);
126
+ });
127
+ }
102
128
  // GET not empty
103
129
  test('getByIds all items', async () => {
104
130
  const rows = await dao.getByIds(items.map(i => i.id).concat('abcd'));
@@ -4,7 +4,6 @@ exports.runCommonDBTest = runCommonDBTest;
4
4
  exports.expectMatch = expectMatch;
5
5
  const js_lib_1 = require("@naturalcycles/js-lib");
6
6
  const common_db_1 = require("../common.db");
7
- const db_model_1 = require("../db.model");
8
7
  const dbQuery_1 = require("../query/dbQuery");
9
8
  const test_model_1 = require("./test.model");
10
9
  function runCommonDBTest(db, quirks = {}) {
@@ -236,8 +235,8 @@ function runCommonDBTest(db, quirks = {}) {
236
235
  expectMatch(expected, rows, quirks);
237
236
  });
238
237
  }
239
- if (support.updateByQuery) {
240
- test('updateByQuery simple', async () => {
238
+ if (support.patchByQuery) {
239
+ test('patchByQuery', async () => {
241
240
  // cleanup, reset initial data
242
241
  await db.deleteByQuery(queryAll());
243
242
  await db.saveBatch(test_model_1.TEST_TABLE, items);
@@ -245,7 +244,7 @@ function runCommonDBTest(db, quirks = {}) {
245
244
  k3: 5,
246
245
  k2: 'abc',
247
246
  };
248
- await db.updateByQuery(dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE).filterEq('even', true), patch);
247
+ await db.patchByQuery(dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE).filterEq('even', true), patch);
249
248
  const { rows } = await db.runQuery(queryAll());
250
249
  const expected = items.map(r => {
251
250
  if (r.even) {
@@ -255,26 +254,25 @@ function runCommonDBTest(db, quirks = {}) {
255
254
  });
256
255
  expectMatch(expected, rows, quirks);
257
256
  });
258
- if (support.dbIncrement) {
259
- test('updateByQuery DBIncrement', async () => {
260
- // cleanup, reset initial data
261
- await db.deleteByQuery(queryAll());
262
- await db.saveBatch(test_model_1.TEST_TABLE, items);
263
- const patch = {
264
- k3: db_model_1.DBIncrement.of(1),
265
- k2: 'abcd',
266
- };
267
- await db.updateByQuery(dbQuery_1.DBQuery.create(test_model_1.TEST_TABLE).filterEq('even', true), patch);
268
- const { rows } = await db.runQuery(queryAll());
269
- const expected = items.map(r => {
270
- if (r.even) {
271
- return { ...r, ...patch, k3: (r.k3 || 0) + 1 };
272
- }
273
- return r;
274
- });
275
- expectMatch(expected, rows, quirks);
257
+ }
258
+ if (support.increment) {
259
+ test('incrementBatch', async () => {
260
+ // cleanup, reset initial data
261
+ await db.deleteByQuery(queryAll());
262
+ await db.saveBatch(test_model_1.TEST_TABLE, items);
263
+ await db.incrementBatch(test_model_1.TEST_TABLE, 'k3', { id1: 1, id2: 2 });
264
+ const { rows } = await db.runQuery(queryAll());
265
+ const expected = items.map(r => {
266
+ if (r.id === 'id1') {
267
+ return { ...r, k3: 2 };
268
+ }
269
+ if (r.id === 'id2') {
270
+ return { ...r, k3: 4 };
271
+ }
272
+ return r;
276
273
  });
277
- }
274
+ expectMatch(expected, rows, quirks);
275
+ });
278
276
  }
279
277
  if (support.queries) {
280
278
  test('cleanup', async () => {