@naturalcycles/db-lib 8.28.0 → 8.30.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.
@@ -13,11 +13,11 @@ import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoSaveOp
13
13
  export declare class CommonDao<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId = Saved<BM>, TM = BM> {
14
14
  cfg: CommonDaoCfg<BM, DBM, TM>;
15
15
  constructor(cfg: CommonDaoCfg<BM, DBM, TM>);
16
- create(part: Partial<BM>, opt?: CommonDaoOptions): Saved<BM>;
16
+ create(part?: Partial<BM>, opt?: CommonDaoOptions): Saved<BM>;
17
17
  getById(id: undefined, opt?: CommonDaoOptions): Promise<null>;
18
18
  getById(id?: string, opt?: CommonDaoOptions): Promise<Saved<BM> | null>;
19
- getByIdOrEmpty(id: string, part: Partial<BM>, opt?: CommonDaoOptions): Promise<Saved<BM>>;
20
- getByIdAsDBMOrEmpty(id: string, part: Partial<BM>, opt?: CommonDaoOptions): Promise<DBM>;
19
+ getByIdOrEmpty(id: string, part?: Partial<BM>, opt?: CommonDaoOptions): Promise<Saved<BM>>;
20
+ getByIdAsDBMOrEmpty(id: string, part?: Partial<BM>, opt?: CommonDaoOptions): Promise<DBM>;
21
21
  getByIdAsDBM(id: undefined, opt?: CommonDaoOptions): Promise<null>;
22
22
  getByIdAsDBM(id?: string, opt?: CommonDaoOptions): Promise<DBM | null>;
23
23
  getByIdAsTM(id: undefined, opt?: CommonDaoOptions): Promise<null>;
@@ -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.
@@ -44,7 +44,7 @@ class CommonDao {
44
44
  };
45
45
  }
46
46
  // CREATE
47
- create(part, opt = {}) {
47
+ create(part = {}, opt = {}) {
48
48
  let bm = this.cfg.hooks.beforeCreate(part);
49
49
  bm = this.validateAndConvert(bm, this.cfg.bmSchema, db_model_1.DBModelType.BM, opt);
50
50
  // If no SCHEMA - return as is
@@ -71,13 +71,13 @@ class CommonDao {
71
71
  this.logResult(started, op, bm, table);
72
72
  return bm || null;
73
73
  }
74
- async getByIdOrEmpty(id, part, opt) {
74
+ async getByIdOrEmpty(id, part = {}, opt) {
75
75
  const bm = await this.getById(id, opt);
76
76
  if (bm)
77
77
  return bm;
78
78
  return this.create({ ...part, id }, opt);
79
79
  }
80
- async getByIdAsDBMOrEmpty(id, part, opt) {
80
+ async getByIdAsDBMOrEmpty(id, part = {}, opt) {
81
81
  const dbm = await this.getByIdAsDBM(id, opt);
82
82
  if (dbm)
83
83
  return dbm;
@@ -184,6 +184,12 @@ class CommonDao {
184
184
  const { rows } = await this.runQueryExtended(q, opt);
185
185
  return rows;
186
186
  }
187
+ async runQuerySingleColumn(q, opt) {
188
+ (0, js_lib_1._assert)(q._selectedFieldNames?.length === 1, `runQuerySingleColumn requires exactly 1 column to be selected: ${q.pretty()}`);
189
+ const col = q._selectedFieldNames[0];
190
+ const { rows } = await this.runQueryExtended(q, opt);
191
+ return rows.map(r => r[col]);
192
+ }
187
193
  /**
188
194
  * Convenience method that runs multiple queries in parallel and then merges their results together.
189
195
  * Does deduplication by id.
@@ -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
  }
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "engines": {
44
44
  "node": ">=14.15"
45
45
  },
46
- "version": "8.28.0",
46
+ "version": "8.30.0",
47
47
  "description": "Lowest Common Denominator API to supported Databases",
48
48
  "keywords": [
49
49
  "db",
@@ -1,4 +1,5 @@
1
1
  import {
2
+ _assert,
2
3
  _filterNullishValues,
3
4
  _filterUndefinedValues,
4
5
  _passthroughPredicate,
@@ -88,7 +89,7 @@ export class CommonDao<
88
89
  }
89
90
 
90
91
  // CREATE
91
- create(part: Partial<BM>, opt: CommonDaoOptions = {}): Saved<BM> {
92
+ create(part: Partial<BM> = {}, opt: CommonDaoOptions = {}): Saved<BM> {
92
93
  let bm = this.cfg.hooks!.beforeCreate!(part) as BM
93
94
  bm = this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
94
95
 
@@ -124,14 +125,22 @@ export class CommonDao<
124
125
  return bm || null
125
126
  }
126
127
 
127
- async getByIdOrEmpty(id: string, part: Partial<BM>, opt?: CommonDaoOptions): Promise<Saved<BM>> {
128
+ async getByIdOrEmpty(
129
+ id: string,
130
+ part: Partial<BM> = {},
131
+ opt?: CommonDaoOptions,
132
+ ): Promise<Saved<BM>> {
128
133
  const bm = await this.getById(id, opt)
129
134
  if (bm) return bm
130
135
 
131
136
  return this.create({ ...part, id }, opt)
132
137
  }
133
138
 
134
- async getByIdAsDBMOrEmpty(id: string, part: Partial<BM>, opt?: CommonDaoOptions): Promise<DBM> {
139
+ async getByIdAsDBMOrEmpty(
140
+ id: string,
141
+ part: Partial<BM> = {},
142
+ opt?: CommonDaoOptions,
143
+ ): Promise<DBM> {
135
144
  const dbm = await this.getByIdAsDBM(id, opt)
136
145
  if (dbm) return dbm
137
146
 
@@ -254,6 +263,18 @@ export class CommonDao<
254
263
  return rows
255
264
  }
256
265
 
266
+ async runQuerySingleColumn<T = any>(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<T[]> {
267
+ _assert(
268
+ q._selectedFieldNames?.length === 1,
269
+ `runQuerySingleColumn requires exactly 1 column to be selected: ${q.pretty()}`,
270
+ )
271
+
272
+ const col = q._selectedFieldNames[0]!
273
+
274
+ const { rows } = await this.runQueryExtended(q, opt)
275
+ return rows.map(r => r[col as any])
276
+ }
277
+
257
278
  /**
258
279
  * Convenience method that runs multiple queries in parallel and then merges their results together.
259
280
  * Does deduplication by id.
@@ -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
  }