@naturalcycles/db-lib 8.28.2 → 8.32.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.
@@ -39,6 +39,7 @@ export declare class CommonDao<BM extends Partial<ObjectWithId>, DBM extends Obj
39
39
  */
40
40
  query(table?: string): RunnableDBQuery<BM, DBM, TM>;
41
41
  runQuery(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<Saved<BM>[]>;
42
+ runQuerySingleColumn<T = any>(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<T[]>;
42
43
  /**
43
44
  * Convenience method that runs multiple queries in parallel and then merges their results together.
44
45
  * Does deduplication by id.
@@ -25,7 +25,8 @@ class CommonDao {
25
25
  // otherwise to log Operations
26
26
  // e.g in Dev (local machine), Test - it will log operations (useful for debugging)
27
27
  logLevel: isGAE || isCI ? common_dao_model_1.CommonDaoLogLevel.NONE : common_dao_model_1.CommonDaoLogLevel.OPERATIONS,
28
- createdUpdated: true,
28
+ created: true,
29
+ updated: true,
29
30
  logger: console,
30
31
  ...cfg,
31
32
  hooks: {
@@ -184,6 +185,12 @@ class CommonDao {
184
185
  const { rows } = await this.runQueryExtended(q, opt);
185
186
  return rows;
186
187
  }
188
+ async runQuerySingleColumn(q, opt) {
189
+ (0, js_lib_1._assert)(q._selectedFieldNames?.length === 1, `runQuerySingleColumn requires exactly 1 column to be selected: ${q.pretty()}`);
190
+ const col = q._selectedFieldNames[0];
191
+ const { rows } = await this.runQueryExtended(q, opt);
192
+ return rows.map(r => r[col]);
193
+ }
187
194
  /**
188
195
  * Convenience method that runs multiple queries in parallel and then merges their results together.
189
196
  * Does deduplication by id.
@@ -399,9 +406,13 @@ class CommonDao {
399
406
  assignIdCreatedUpdated(obj, opt = {}) {
400
407
  const now = Math.floor(Date.now() / 1000);
401
408
  obj.id = obj.id || this.cfg.hooks.createId(obj);
402
- if (this.cfg.createdUpdated) {
409
+ if (this.cfg.created) {
403
410
  Object.assign(obj, {
404
411
  created: obj.created || obj.updated || now,
412
+ });
413
+ }
414
+ if (this.cfg.updated) {
415
+ Object.assign(obj, {
405
416
  updated: opt.preserveUpdatedCreated && obj.updated ? obj.updated : now,
406
417
  });
407
418
  }
@@ -76,10 +76,15 @@ export interface CommonDaoCfg<BM extends Partial<ObjectWithId>, DBM extends Obje
76
76
  logStarted?: boolean;
77
77
  hooks?: Partial<CommonDaoHooks<BM, DBM, TM>>;
78
78
  /**
79
- * @default true
80
- * Set to false to disable created/updated fields management.
79
+ * Defaults to true
80
+ * Set to false to disable `created` field management.
81
81
  */
82
- createdUpdated?: boolean;
82
+ created?: boolean;
83
+ /**
84
+ * Defaults to true
85
+ * Set to false to disable `updated` field management.
86
+ */
87
+ updated?: boolean;
83
88
  /**
84
89
  * Default is false.
85
90
  * If true - will run `_filterNullishValues` inside `validateAndConvert` function
@@ -33,6 +33,7 @@ export declare class CommonKeyValueDao<T> {
33
33
  createTable(opt?: CommonDBCreateOptions): Promise<void>;
34
34
  create(input?: Partial<T>): T;
35
35
  getById(id?: string): Promise<T | null>;
36
+ requireById(id: string): Promise<T>;
36
37
  getByIdOrEmpty(id: string, part?: Partial<T>): Promise<T>;
37
38
  patch(id: string, patch: Partial<T>): Promise<T>;
38
39
  getByIds(ids: string[]): Promise<KeyValueTuple<string, T>[]>;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CommonKeyValueDao = void 0;
4
4
  const js_lib_1 = require("@naturalcycles/js-lib");
5
5
  const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
6
+ const cnst_1 = require("../cnst");
6
7
  // todo: logging
7
8
  // todo: readonly
8
9
  class CommonKeyValueDao {
@@ -26,6 +27,18 @@ class CommonKeyValueDao {
26
27
  const [r] = await this.getByIds([id]);
27
28
  return r?.[1] || null;
28
29
  }
30
+ async requireById(id) {
31
+ const [r] = await this.getByIds([id]);
32
+ if (!r) {
33
+ const { table } = this.cfg;
34
+ throw new js_lib_1.AppError(`DB row required, but not found: ${table}.${id}`, {
35
+ code: cnst_1.DBLibError.DB_ROW_REQUIRED,
36
+ table,
37
+ id,
38
+ });
39
+ }
40
+ return r[1];
41
+ }
29
42
  async getByIdOrEmpty(id, part = {}) {
30
43
  const [r] = await this.getByIds([id]);
31
44
  if (r)
@@ -64,12 +64,16 @@ export declare class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
64
64
  * In undefined - all fields (*) will be returned.
65
65
  */
66
66
  _selectedFieldNames?: (keyof ROW)[];
67
+ _groupByFieldNames?: (keyof ROW)[];
68
+ _distinct: boolean;
67
69
  filter(name: keyof ROW, op: DBQueryFilterOperator, val: any): this;
68
70
  filterEq(name: keyof ROW, val: any): this;
69
71
  limit(limit: number): this;
70
72
  offset(offset: number): this;
71
73
  order(name: keyof ROW, descending?: boolean): this;
72
74
  select(fieldNames: (keyof ROW)[]): this;
75
+ groupBy(fieldNames: (keyof ROW)[]): this;
76
+ distinct(distinct?: boolean): this;
73
77
  startCursor(startCursor?: string): this;
74
78
  endCursor(endCursor?: string): this;
75
79
  clone(): DBQuery<ROW>;
@@ -86,6 +90,7 @@ export declare class RunnableDBQuery<BM extends Partial<ObjectWithId>, DBM exten
86
90
  */
87
91
  constructor(dao: CommonDao<BM, DBM, TM>, table?: string);
88
92
  runQuery(opt?: CommonDaoOptions): Promise<Saved<BM>[]>;
93
+ runQuerySingleColumn<T = any>(opt?: CommonDaoOptions): Promise<T[]>;
89
94
  runQueryAsDBM(opt?: CommonDaoOptions): Promise<DBM[]>;
90
95
  runQueryAsTM(opt?: CommonDaoOptions): Promise<TM[]>;
91
96
  runQueryExtended(opt?: CommonDaoOptions): Promise<RunQueryResult<Saved<BM>>>;
@@ -30,6 +30,7 @@ class DBQuery {
30
30
  this._limitValue = 0; // 0 means "no limit"
31
31
  this._offsetValue = 0; // 0 means "no offset"
32
32
  this._orders = [];
33
+ this._distinct = false;
33
34
  }
34
35
  /**
35
36
  * Convenience method.
@@ -67,6 +68,14 @@ class DBQuery {
67
68
  this._selectedFieldNames = fieldNames;
68
69
  return this;
69
70
  }
71
+ groupBy(fieldNames) {
72
+ this._groupByFieldNames = fieldNames;
73
+ return this;
74
+ }
75
+ distinct(distinct = true) {
76
+ this._distinct = distinct;
77
+ return this;
78
+ }
70
79
  startCursor(startCursor) {
71
80
  this._startCursor = startCursor;
72
81
  return this;
@@ -82,6 +91,8 @@ class DBQuery {
82
91
  _offsetValue: this._offsetValue,
83
92
  _orders: [...this._orders],
84
93
  _selectedFieldNames: this._selectedFieldNames && [...this._selectedFieldNames],
94
+ _groupByFieldNames: this._groupByFieldNames && [...this._groupByFieldNames],
95
+ _distinct: this._distinct,
85
96
  _startCursor: this._startCursor,
86
97
  _endCursor: this._endCursor,
87
98
  });
@@ -95,9 +106,12 @@ class DBQuery {
95
106
  // tokens.push(`"${this.name}"`)
96
107
  // }
97
108
  if (this._selectedFieldNames) {
98
- tokens.push(`select(${this._selectedFieldNames.join(',')})`);
109
+ tokens.push(`select${this._distinct ? ' distinct' : ''}(${this._selectedFieldNames.join(',')})`);
99
110
  }
100
111
  tokens.push(...this._filters.map(f => `${f.name}${f.op}${f.val}`), ...this._orders.map(o => `order by ${o.name}${o.descending ? ' desc' : ''}`));
112
+ if (this._groupByFieldNames) {
113
+ tokens.push(`groupBy(${this._groupByFieldNames.join(',')})`);
114
+ }
101
115
  if (this._offsetValue) {
102
116
  tokens.push(`offset ${this._offsetValue}`);
103
117
  }
@@ -128,6 +142,9 @@ class RunnableDBQuery extends DBQuery {
128
142
  async runQuery(opt) {
129
143
  return await this.dao.runQuery(this, opt);
130
144
  }
145
+ async runQuerySingleColumn(opt) {
146
+ return await this.dao.runQuerySingleColumn(this, opt);
147
+ }
131
148
  async runQueryAsDBM(opt) {
132
149
  return await this.dao.runQueryAsDBM(this, opt);
133
150
  }
@@ -17,7 +17,7 @@ export interface TestItemTM {
17
17
  export declare const testItemBMSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<TestItemBM, TestItemBM>;
18
18
  export declare const testItemDBMSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<TestItemDBM, TestItemDBM>;
19
19
  export declare const testItemTMSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<TestItemTM, TestItemTM>;
20
- export declare const testItemBMJsonSchema: import("@naturalcycles/js-lib/dist/json-schema/jsonSchemaBuilder").JsonSchemaObjectBuilder<TestItemBM & Partial<import("@naturalcycles/js-lib").SavedDBEntity>>;
20
+ export declare const testItemBMJsonSchema: import("@naturalcycles/js-lib/dist/json-schema/jsonSchemaBuilder").JsonSchemaObjectBuilder<TestItemBM & Partial<import("@naturalcycles/js-lib").SavedDBEntity<string>>>;
21
21
  export declare const testItemDBMJsonSchema: import("@naturalcycles/js-lib/dist/json-schema/jsonSchemaBuilder").JsonSchemaObjectBuilder<TestItemDBM>;
22
22
  export declare function createTestItemDBM(num?: number): TestItemDBM;
23
23
  export declare function createTestItemBM(num?: number): Saved<TestItemBM>;
@@ -1,8 +1,8 @@
1
1
  import { CommonDBOptions, CommonDBSaveOptions } from '../db.model';
2
2
  import { DBQuery, DBQueryFilter, DBQueryOrder } from '../query/dbQuery';
3
3
  export declare const commonDBOptionsSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<CommonDBOptions, CommonDBOptions>;
4
- export declare const commonDBSaveOptionsSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<CommonDBSaveOptions<import("@naturalcycles/js-lib").AnyObjectWithId>, CommonDBSaveOptions<import("@naturalcycles/js-lib").AnyObjectWithId>>;
4
+ export declare const commonDBSaveOptionsSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<CommonDBSaveOptions<import("@naturalcycles/js-lib").AnyObjectWithId<string>>, CommonDBSaveOptions<import("@naturalcycles/js-lib").AnyObjectWithId<string>>>;
5
5
  export declare const dbQueryFilterOperatorSchema: import("@naturalcycles/nodejs-lib/dist/validation/joi/string.extensions").ExtendedStringSchema;
6
- export declare const dbQueryFilterSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<DBQueryFilter<import("@naturalcycles/js-lib").AnyObjectWithId>, DBQueryFilter<import("@naturalcycles/js-lib").AnyObjectWithId>>;
7
- export declare const dbQueryOrderSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<DBQueryOrder<import("@naturalcycles/js-lib").AnyObjectWithId>, DBQueryOrder<import("@naturalcycles/js-lib").AnyObjectWithId>>;
6
+ export declare const dbQueryFilterSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<DBQueryFilter<import("@naturalcycles/js-lib").AnyObjectWithId<string>>, DBQueryFilter<import("@naturalcycles/js-lib").AnyObjectWithId<string>>>;
7
+ export declare const dbQueryOrderSchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<DBQueryOrder<import("@naturalcycles/js-lib").AnyObjectWithId<string>>, DBQueryOrder<import("@naturalcycles/js-lib").AnyObjectWithId<string>>>;
8
8
  export declare const dbQuerySchema: import("@naturalcycles/nodejs-lib").ObjectSchemaTyped<DBQuery<any>, DBQuery<any>>;
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "engines": {
44
44
  "node": ">=14.15"
45
45
  },
46
- "version": "8.28.2",
46
+ "version": "8.32.0",
47
47
  "description": "Lowest Common Denominator API to supported Databases",
48
48
  "keywords": [
49
49
  "db",
@@ -102,10 +102,16 @@ export interface CommonDaoCfg<
102
102
  hooks?: Partial<CommonDaoHooks<BM, DBM, TM>>
103
103
 
104
104
  /**
105
- * @default true
106
- * Set to false to disable created/updated fields management.
105
+ * Defaults to true
106
+ * Set to false to disable `created` field management.
107
107
  */
108
- createdUpdated?: boolean
108
+ created?: boolean
109
+
110
+ /**
111
+ * Defaults to true
112
+ * Set to false to disable `updated` field management.
113
+ */
114
+ updated?: boolean
109
115
 
110
116
  /**
111
117
  * Default is false.
@@ -1,4 +1,5 @@
1
1
  import {
2
+ _assert,
2
3
  _filterNullishValues,
3
4
  _filterUndefinedValues,
4
5
  _passthroughPredicate,
@@ -68,7 +69,8 @@ export class CommonDao<
68
69
  // otherwise to log Operations
69
70
  // e.g in Dev (local machine), Test - it will log operations (useful for debugging)
70
71
  logLevel: isGAE || isCI ? CommonDaoLogLevel.NONE : CommonDaoLogLevel.OPERATIONS,
71
- createdUpdated: true,
72
+ created: true,
73
+ updated: true,
72
74
  logger: console,
73
75
  ...cfg,
74
76
  hooks: {
@@ -262,6 +264,18 @@ export class CommonDao<
262
264
  return rows
263
265
  }
264
266
 
267
+ async runQuerySingleColumn<T = any>(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<T[]> {
268
+ _assert(
269
+ q._selectedFieldNames?.length === 1,
270
+ `runQuerySingleColumn requires exactly 1 column to be selected: ${q.pretty()}`,
271
+ )
272
+
273
+ const col = q._selectedFieldNames[0]!
274
+
275
+ const { rows } = await this.runQueryExtended(q, opt)
276
+ return rows.map(r => r[col as any])
277
+ }
278
+
265
279
  /**
266
280
  * Convenience method that runs multiple queries in parallel and then merges their results together.
267
281
  * Does deduplication by id.
@@ -549,9 +563,14 @@ export class CommonDao<
549
563
 
550
564
  obj.id = obj.id || this.cfg.hooks!.createId!(obj)
551
565
 
552
- if (this.cfg.createdUpdated) {
566
+ if (this.cfg.created) {
553
567
  Object.assign(obj, {
554
568
  created: (obj as any).created || (obj as any).updated || now,
569
+ })
570
+ }
571
+
572
+ if (this.cfg.updated) {
573
+ Object.assign(obj, {
555
574
  updated: opt.preserveUpdatedCreated && (obj as any).updated ? (obj as any).updated : now,
556
575
  })
557
576
  }
@@ -1,5 +1,6 @@
1
- import { ErrorMode, KeyValueTuple, pMap } from '@naturalcycles/js-lib'
1
+ import { AppError, ErrorMode, KeyValueTuple, pMap } from '@naturalcycles/js-lib'
2
2
  import { ReadableTyped, transformMap } from '@naturalcycles/nodejs-lib'
3
+ import { DBLibError } from '../cnst'
3
4
  import { CommonDaoLogLevel } from '../commondao/common.dao.model'
4
5
  import { CommonDBCreateOptions } from '../db.model'
5
6
  import { CommonKeyValueDB, KeyValueDBTuple } from './commonKeyValueDB'
@@ -58,6 +59,21 @@ export class CommonKeyValueDao<T> {
58
59
  return r?.[1] || null
59
60
  }
60
61
 
62
+ async requireById(id: string): Promise<T> {
63
+ const [r] = await this.getByIds([id])
64
+
65
+ if (!r) {
66
+ const { table } = this.cfg
67
+ throw new AppError(`DB row required, but not found: ${table}.${id}`, {
68
+ code: DBLibError.DB_ROW_REQUIRED,
69
+ table,
70
+ id,
71
+ })
72
+ }
73
+
74
+ return r[1]
75
+ }
76
+
61
77
  async getByIdOrEmpty(id: string, part: Partial<T> = {}): Promise<T> {
62
78
  const [r] = await this.getByIds([id])
63
79
  if (r) return r[1]
@@ -96,6 +96,8 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
96
96
  * In undefined - all fields (*) will be returned.
97
97
  */
98
98
  _selectedFieldNames?: (keyof ROW)[]
99
+ _groupByFieldNames?: (keyof ROW)[]
100
+ _distinct = false
99
101
 
100
102
  filter(name: keyof ROW, op: DBQueryFilterOperator, val: any): this {
101
103
  this._filters.push({ name, op, val })
@@ -130,6 +132,16 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
130
132
  return this
131
133
  }
132
134
 
135
+ groupBy(fieldNames: (keyof ROW)[]): this {
136
+ this._groupByFieldNames = fieldNames
137
+ return this
138
+ }
139
+
140
+ distinct(distinct = true): this {
141
+ this._distinct = distinct
142
+ return this
143
+ }
144
+
133
145
  startCursor(startCursor?: string): this {
134
146
  this._startCursor = startCursor
135
147
  return this
@@ -147,6 +159,8 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
147
159
  _offsetValue: this._offsetValue,
148
160
  _orders: [...this._orders],
149
161
  _selectedFieldNames: this._selectedFieldNames && [...this._selectedFieldNames],
162
+ _groupByFieldNames: this._groupByFieldNames && [...this._groupByFieldNames],
163
+ _distinct: this._distinct,
150
164
  _startCursor: this._startCursor,
151
165
  _endCursor: this._endCursor,
152
166
  })
@@ -164,7 +178,9 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
164
178
  // }
165
179
 
166
180
  if (this._selectedFieldNames) {
167
- tokens.push(`select(${this._selectedFieldNames.join(',')})`)
181
+ tokens.push(
182
+ `select${this._distinct ? ' distinct' : ''}(${this._selectedFieldNames.join(',')})`,
183
+ )
168
184
  }
169
185
 
170
186
  tokens.push(
@@ -172,6 +188,10 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
172
188
  ...this._orders.map(o => `order by ${o.name}${o.descending ? ' desc' : ''}`),
173
189
  )
174
190
 
191
+ if (this._groupByFieldNames) {
192
+ tokens.push(`groupBy(${this._groupByFieldNames.join(',')})`)
193
+ }
194
+
175
195
  if (this._offsetValue) {
176
196
  tokens.push(`offset ${this._offsetValue}`)
177
197
  }
@@ -211,6 +231,10 @@ export class RunnableDBQuery<
211
231
  return await this.dao.runQuery(this, opt)
212
232
  }
213
233
 
234
+ async runQuerySingleColumn<T = any>(opt?: CommonDaoOptions): Promise<T[]> {
235
+ return await this.dao.runQuerySingleColumn<T>(this, opt)
236
+ }
237
+
214
238
  async runQueryAsDBM(opt?: CommonDaoOptions): Promise<DBM[]> {
215
239
  return await this.dao.runQueryAsDBM(this, opt)
216
240
  }