@naturalcycles/db-lib 8.27.0 → 8.29.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,12 +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(input: 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
- getByIdOrCreate(id: string, bmToCreate: Partial<BM>, opt?: CommonDaoOptions): Promise<Saved<BM>>;
20
- getByIdOrEmpty(id: string, opt?: CommonDaoOptions): Promise<Saved<BM>>;
21
- getByIdAsDBMOrEmpty(id: string, 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>;
22
21
  getByIdAsDBM(id: undefined, opt?: CommonDaoOptions): Promise<null>;
23
22
  getByIdAsDBM(id?: string, opt?: CommonDaoOptions): Promise<DBM | null>;
24
23
  getByIdAsTM(id: undefined, opt?: CommonDaoOptions): Promise<null>;
@@ -44,8 +44,8 @@ class CommonDao {
44
44
  };
45
45
  }
46
46
  // CREATE
47
- create(input, opt = {}) {
48
- let bm = this.cfg.hooks.beforeCreate(input);
47
+ create(part = {}, opt = {}) {
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
51
51
  return this.assignIdCreatedUpdated(bm, opt);
@@ -71,23 +71,17 @@ class CommonDao {
71
71
  this.logResult(started, op, bm, table);
72
72
  return bm || null;
73
73
  }
74
- async getByIdOrCreate(id, bmToCreate, 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
- return this.create({ ...bmToCreate, id }, opt);
78
+ return this.create({ ...part, id }, opt);
79
79
  }
80
- async getByIdOrEmpty(id, opt) {
81
- const bm = await this.getById(id, opt);
82
- if (bm)
83
- return bm;
84
- return this.create({ id }, opt);
85
- }
86
- async getByIdAsDBMOrEmpty(id, opt) {
80
+ async getByIdAsDBMOrEmpty(id, part = {}, opt) {
87
81
  const dbm = await this.getByIdAsDBM(id, opt);
88
82
  if (dbm)
89
83
  return dbm;
90
- const bm = this.create({ id }, opt);
84
+ const bm = this.create({ ...part, id }, opt);
91
85
  return await this.bmToDBM(bm, opt);
92
86
  }
93
87
  async getByIdAsDBM(id, opt = {}) {
@@ -458,7 +452,7 @@ class CommonDao {
458
452
  */
459
453
  async patch(id, patch, opt = {}) {
460
454
  return await this.save({
461
- ...(await this.getByIdOrCreate(id, patch, opt)),
455
+ ...(await this.getByIdOrEmpty(id, patch, opt)),
462
456
  ...patch,
463
457
  }, opt);
464
458
  }
@@ -4,7 +4,7 @@ import { CommonDB } from '../common.db';
4
4
  import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model';
5
5
  export declare type CommonDaoCreateIdHook<BM, DBM> = (obj: DBM | BM) => string;
6
6
  export declare type CommonDaoParseNaturalIdHook<DBM> = (id: string) => Partial<DBM>;
7
- export declare type CommonDaoBeforeCreateHook<BM> = (bm: Partial<BM>) => BM;
7
+ export declare type CommonDaoBeforeCreateHook<BM> = (bm: Partial<BM>) => Partial<BM>;
8
8
  export declare type CommonDaoBeforeDBMValidateHook<DBM> = (dbm: Partial<DBM>) => Partial<DBM>;
9
9
  export declare type CommonDaoBeforeDBMToBMHook<BM, DBM> = (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>;
10
10
  export declare type CommonDaoBeforeBMToDBMHook<BM, DBM> = (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>;
@@ -21,8 +21,9 @@ export interface CommonKeyValueDaoCfg<T> {
21
21
  */
22
22
  logStarted?: boolean;
23
23
  hooks?: {
24
- mapValueToBuffer(v: T): Promise<Buffer>;
25
- mapBufferToValue(b: Buffer): Promise<T>;
24
+ mapValueToBuffer?: (v: T) => Promise<Buffer>;
25
+ mapBufferToValue?: (b: Buffer) => Promise<T>;
26
+ beforeCreate?: (v: Partial<T>) => Partial<T>;
26
27
  };
27
28
  }
28
29
  export declare class CommonKeyValueDao<T> {
@@ -30,7 +31,10 @@ export declare class CommonKeyValueDao<T> {
30
31
  constructor(cfg: CommonKeyValueDaoCfg<T>);
31
32
  ping(): Promise<void>;
32
33
  createTable(opt?: CommonDBCreateOptions): Promise<void>;
34
+ create(input?: Partial<T>): T;
33
35
  getById(id?: string): Promise<T | null>;
36
+ getByIdOrEmpty(id: string, part?: Partial<T>): Promise<T>;
37
+ patch(id: string, patch: Partial<T>): Promise<T>;
34
38
  getByIds(ids: string[]): Promise<KeyValueTuple<string, T>[]>;
35
39
  save(id: string, value: T): Promise<void>;
36
40
  saveBatch(entries: KeyValueTuple<string, T>[]): Promise<void>;
@@ -15,12 +15,34 @@ class CommonKeyValueDao {
15
15
  async createTable(opt = {}) {
16
16
  await this.cfg.db.createTable(this.cfg.table, opt);
17
17
  }
18
+ create(input = {}) {
19
+ return {
20
+ ...this.cfg.hooks?.beforeCreate?.(input),
21
+ };
22
+ }
18
23
  async getById(id) {
19
24
  if (!id)
20
25
  return null;
21
26
  const [r] = await this.getByIds([id]);
22
27
  return r?.[1] || null;
23
28
  }
29
+ async getByIdOrEmpty(id, part = {}) {
30
+ const [r] = await this.getByIds([id]);
31
+ if (r)
32
+ return r[1];
33
+ return {
34
+ ...this.cfg.hooks?.beforeCreate?.({}),
35
+ ...part,
36
+ };
37
+ }
38
+ async patch(id, patch) {
39
+ const v = {
40
+ ...(await this.getByIdOrEmpty(id)),
41
+ ...patch,
42
+ };
43
+ await this.save(id, v);
44
+ return v;
45
+ }
24
46
  async getByIds(ids) {
25
47
  const entries = await this.cfg.db.getByIds(this.cfg.table, ids);
26
48
  if (!this.cfg.hooks?.mapBufferToValue)
@@ -64,12 +64,14 @@ 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)[];
67
68
  filter(name: keyof ROW, op: DBQueryFilterOperator, val: any): this;
68
69
  filterEq(name: keyof ROW, val: any): this;
69
70
  limit(limit: number): this;
70
71
  offset(offset: number): this;
71
72
  order(name: keyof ROW, descending?: boolean): this;
72
73
  select(fieldNames: (keyof ROW)[]): this;
74
+ groupBy(fieldNames: (keyof ROW)[]): this;
73
75
  startCursor(startCursor?: string): this;
74
76
  endCursor(endCursor?: string): this;
75
77
  clone(): DBQuery<ROW>;
@@ -67,6 +67,10 @@ class DBQuery {
67
67
  this._selectedFieldNames = fieldNames;
68
68
  return this;
69
69
  }
70
+ groupBy(fieldNames) {
71
+ this._groupByFieldNames = fieldNames;
72
+ return this;
73
+ }
70
74
  startCursor(startCursor) {
71
75
  this._startCursor = startCursor;
72
76
  return this;
@@ -98,6 +102,9 @@ class DBQuery {
98
102
  tokens.push(`select(${this._selectedFieldNames.join(',')})`);
99
103
  }
100
104
  tokens.push(...this._filters.map(f => `${f.name}${f.op}${f.val}`), ...this._orders.map(o => `order by ${o.name}${o.descending ? ' desc' : ''}`));
105
+ if (this._groupByFieldNames) {
106
+ tokens.push(`groupBy(${this._groupByFieldNames.join(',')})`);
107
+ }
101
108
  if (this._offsetValue) {
102
109
  tokens.push(`offset ${this._offsetValue}`);
103
110
  }
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "engines": {
44
44
  "node": ">=14.15"
45
45
  },
46
- "version": "8.27.0",
46
+ "version": "8.29.0",
47
47
  "description": "Lowest Common Denominator API to supported Databases",
48
48
  "keywords": [
49
49
  "db",
@@ -13,7 +13,7 @@ import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../
13
13
  // Hook DBM, BM, TM types should follow this exact order
14
14
  export type CommonDaoCreateIdHook<BM, DBM> = (obj: DBM | BM) => string
15
15
  export type CommonDaoParseNaturalIdHook<DBM> = (id: string) => Partial<DBM>
16
- export type CommonDaoBeforeCreateHook<BM> = (bm: Partial<BM>) => BM
16
+ export type CommonDaoBeforeCreateHook<BM> = (bm: Partial<BM>) => Partial<BM>
17
17
  export type CommonDaoBeforeDBMValidateHook<DBM> = (dbm: Partial<DBM>) => Partial<DBM>
18
18
  export type CommonDaoBeforeDBMToBMHook<BM, DBM> = (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>
19
19
  export type CommonDaoBeforeBMToDBMHook<BM, DBM> = (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>
@@ -88,8 +88,8 @@ export class CommonDao<
88
88
  }
89
89
 
90
90
  // CREATE
91
- create(input: Partial<BM>, opt: CommonDaoOptions = {}): Saved<BM> {
92
- let bm = this.cfg.hooks!.beforeCreate!(input)
91
+ create(part: Partial<BM> = {}, opt: CommonDaoOptions = {}): Saved<BM> {
92
+ let bm = this.cfg.hooks!.beforeCreate!(part) as BM
93
93
  bm = this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
94
94
 
95
95
  // If no SCHEMA - return as is
@@ -124,29 +124,26 @@ export class CommonDao<
124
124
  return bm || null
125
125
  }
126
126
 
127
- async getByIdOrCreate(
127
+ async getByIdOrEmpty(
128
128
  id: string,
129
- bmToCreate: Partial<BM>,
129
+ part: Partial<BM> = {},
130
130
  opt?: CommonDaoOptions,
131
131
  ): Promise<Saved<BM>> {
132
132
  const bm = await this.getById(id, opt)
133
133
  if (bm) return bm
134
134
 
135
- return this.create({ ...bmToCreate, id }, opt)
135
+ return this.create({ ...part, id }, opt)
136
136
  }
137
137
 
138
- async getByIdOrEmpty(id: string, opt?: CommonDaoOptions): Promise<Saved<BM>> {
139
- const bm = await this.getById(id, opt)
140
- if (bm) return bm
141
-
142
- return this.create({ id } as Partial<BM>, opt)
143
- }
144
-
145
- async getByIdAsDBMOrEmpty(id: string, opt?: CommonDaoOptions): Promise<DBM> {
138
+ async getByIdAsDBMOrEmpty(
139
+ id: string,
140
+ part: Partial<BM> = {},
141
+ opt?: CommonDaoOptions,
142
+ ): Promise<DBM> {
146
143
  const dbm = await this.getByIdAsDBM(id, opt)
147
144
  if (dbm) return dbm
148
145
 
149
- const bm: BM = this.create({ id } as Partial<BM>, opt) as any
146
+ const bm: BM = this.create({ ...part, id }, opt) as any
150
147
  return await this.bmToDBM(bm, opt)
151
148
  }
152
149
 
@@ -614,7 +611,7 @@ export class CommonDao<
614
611
  ): Promise<Saved<BM>> {
615
612
  return await this.save(
616
613
  {
617
- ...(await this.getByIdOrCreate(id, patch, opt)),
614
+ ...(await this.getByIdOrEmpty(id, patch, opt)),
618
615
  ...patch,
619
616
  } as any,
620
617
  opt,
@@ -26,8 +26,9 @@ export interface CommonKeyValueDaoCfg<T> {
26
26
  logStarted?: boolean
27
27
 
28
28
  hooks?: {
29
- mapValueToBuffer(v: T): Promise<Buffer>
30
- mapBufferToValue(b: Buffer): Promise<T>
29
+ mapValueToBuffer?: (v: T) => Promise<Buffer>
30
+ mapBufferToValue?: (b: Buffer) => Promise<T>
31
+ beforeCreate?: (v: Partial<T>) => Partial<T>
31
32
  }
32
33
  }
33
34
 
@@ -45,19 +46,46 @@ export class CommonKeyValueDao<T> {
45
46
  await this.cfg.db.createTable(this.cfg.table, opt)
46
47
  }
47
48
 
49
+ create(input: Partial<T> = {}): T {
50
+ return {
51
+ ...this.cfg.hooks?.beforeCreate?.(input),
52
+ } as T
53
+ }
54
+
48
55
  async getById(id?: string): Promise<T | null> {
49
56
  if (!id) return null
50
57
  const [r] = await this.getByIds([id])
51
58
  return r?.[1] || null
52
59
  }
53
60
 
61
+ async getByIdOrEmpty(id: string, part: Partial<T> = {}): Promise<T> {
62
+ const [r] = await this.getByIds([id])
63
+ if (r) return r[1]
64
+
65
+ return {
66
+ ...this.cfg.hooks?.beforeCreate?.({}),
67
+ ...part,
68
+ } as T
69
+ }
70
+
71
+ async patch(id: string, patch: Partial<T>): Promise<T> {
72
+ const v: T = {
73
+ ...(await this.getByIdOrEmpty(id)),
74
+ ...patch,
75
+ }
76
+
77
+ await this.save(id, v)
78
+
79
+ return v
80
+ }
81
+
54
82
  async getByIds(ids: string[]): Promise<KeyValueTuple<string, T>[]> {
55
83
  const entries = await this.cfg.db.getByIds(this.cfg.table, ids)
56
84
  if (!this.cfg.hooks?.mapBufferToValue) return entries as any
57
85
 
58
86
  return await pMap(entries, async ([id, buf]) => [
59
87
  id,
60
- await this.cfg.hooks!.mapBufferToValue(buf),
88
+ await this.cfg.hooks!.mapBufferToValue!(buf),
61
89
  ])
62
90
  }
63
91
 
@@ -73,7 +101,7 @@ export class CommonKeyValueDao<T> {
73
101
  } else {
74
102
  bufferEntries = await pMap(entries, async ([id, v]) => [
75
103
  id,
76
- await this.cfg.hooks!.mapValueToBuffer(v),
104
+ await this.cfg.hooks!.mapValueToBuffer!(v),
77
105
  ])
78
106
  }
79
107
 
@@ -100,7 +128,7 @@ export class CommonKeyValueDao<T> {
100
128
  // todo: consider it when readableMap supports `errorMode: SUPPRESS`
101
129
  // readableMap(this.cfg.db.streamValues(this.cfg.table, limit), async buf => await this.cfg.hooks!.mapBufferToValue(buf))
102
130
  return this.cfg.db.streamValues(this.cfg.table, limit).pipe(
103
- transformMap(async buf => await this.cfg.hooks!.mapBufferToValue(buf), {
131
+ transformMap(async buf => await this.cfg.hooks!.mapBufferToValue!(buf), {
104
132
  errorMode: ErrorMode.SUPPRESS, // cause .pipe cannot propagate errors
105
133
  }),
106
134
  )
@@ -112,7 +140,7 @@ export class CommonKeyValueDao<T> {
112
140
  }
113
141
 
114
142
  return this.cfg.db.streamEntries(this.cfg.table, limit).pipe(
115
- transformMap(async ([id, buf]) => [id, await this.cfg.hooks!.mapBufferToValue(buf)], {
143
+ transformMap(async ([id, buf]) => [id, await this.cfg.hooks!.mapBufferToValue!(buf)], {
116
144
  errorMode: ErrorMode.SUPPRESS, // cause .pipe cannot propagate errors
117
145
  }),
118
146
  )
@@ -96,6 +96,7 @@ 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)[]
99
100
 
100
101
  filter(name: keyof ROW, op: DBQueryFilterOperator, val: any): this {
101
102
  this._filters.push({ name, op, val })
@@ -130,6 +131,11 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
130
131
  return this
131
132
  }
132
133
 
134
+ groupBy(fieldNames: (keyof ROW)[]): this {
135
+ this._groupByFieldNames = fieldNames
136
+ return this
137
+ }
138
+
133
139
  startCursor(startCursor?: string): this {
134
140
  this._startCursor = startCursor
135
141
  return this
@@ -172,6 +178,10 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
172
178
  ...this._orders.map(o => `order by ${o.name}${o.descending ? ' desc' : ''}`),
173
179
  )
174
180
 
181
+ if (this._groupByFieldNames) {
182
+ tokens.push(`groupBy(${this._groupByFieldNames.join(',')})`)
183
+ }
184
+
175
185
  if (this._offsetValue) {
176
186
  tokens.push(`offset ${this._offsetValue}`)
177
187
  }