@trenskow/pged 4.0.9 → 4.1.2

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.
package/index.js CHANGED
@@ -89,7 +89,8 @@ module.exports = exports = class PGed {
89
89
  return result;
90
90
  }
91
91
 
92
- _convertResult(result) {
92
+ _convertResult(result, options) {
93
+ if ((options || {}).format === 'raw') return (result || {}).rows;
93
94
  return ((result || {}).rows || []).map((row) => {
94
95
  let newRow = {};
95
96
  Object.keys(row).forEach((key) => {
@@ -209,7 +210,7 @@ module.exports = exports = class PGed {
209
210
 
210
211
  let result;
211
212
 
212
- const todo = async () => result = this._convertResult(await this._query(query, parameters));
213
+ const todo = async () => result = this._convertResult(await this._query(query, parameters), options);
213
214
 
214
215
  if (this._transactions.always || options.transaction) await this.transaction(todo);
215
216
  else await this.retained(todo);
@@ -225,25 +226,7 @@ module.exports = exports = class PGed {
225
226
  }
226
227
 
227
228
  _queryBuild(table) {
228
- return new QueryBuilder(table, this._options, async (queryBuilder) => {
229
-
230
- const [query, parameters] = queryBuilder._build();
231
-
232
- let result = await this.exec(
233
- query,
234
- parameters,
235
- {
236
- first: queryBuilder._first,
237
- transaction: queryBuilder._transaction
238
- });
239
-
240
- if (['null', 'undefined'].includes(typeof result)) {
241
- result = queryBuilder._defaultResult;
242
- }
243
-
244
- return result;
245
-
246
- });
229
+ return new QueryBuilder(table, this._options, this);
247
230
  }
248
231
 
249
232
  from(table) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trenskow/pged",
3
- "version": "4.0.9",
3
+ "version": "4.1.2",
4
4
  "description": "Just a silly little db management and query builder for PostgreSQL.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,9 +22,9 @@
22
22
  },
23
23
  "homepage": "https://github.com/trenskow/pged#readme",
24
24
  "dependencies": {
25
- "@trenskow/caseit": "^1.1.0",
25
+ "@trenskow/caseit": "^1.1.4",
26
26
  "@trenskow/custom-promise": "^0.10.1",
27
- "pg": "^8.7.1",
27
+ "pg": "^8.7.3",
28
28
  "puqeue": "^1.0.5"
29
29
  }
30
30
  }
package/query-builder.js CHANGED
@@ -2,11 +2,15 @@
2
2
 
3
3
  const
4
4
  caseit = require('@trenskow/caseit'),
5
- CustomPromise = require('@trenskow/custom-promise');
5
+ CustomPromise = require('@trenskow/custom-promise'),
6
+ Puqeue = require('puqeue');
7
+
8
+ const tableInformationQueue = new Puqeue();
9
+ const tableInformation = {};
6
10
 
7
11
  module.exports = exports = class QueryBuilder extends CustomPromise {
8
12
 
9
- constructor(table, options = {}, executor) {
13
+ constructor(table, options = {}, connection) {
10
14
 
11
15
  super();
12
16
 
@@ -36,7 +40,7 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
36
40
 
37
41
  this._offset = 0;
38
42
 
39
- this._executor = executor;
43
+ this._connection = connection;
40
44
 
41
45
  }
42
46
 
@@ -118,9 +122,17 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
118
122
  return this;
119
123
  }
120
124
 
121
- sorted(keys) {
122
- if (!Array.isArray(keys)) keys = keys.split(/, ?/);
123
- this._sortingKeys = keys;
125
+ sorted(sortingKeys) {
126
+ if (typeof sortingKeys === 'string') sortingKeys = sortingKeys.split(/, ?/);
127
+ if (!Array.isArray(sortingKeys)) sortingKeys = [sortingKeys];
128
+ sortingKeys = sortingKeys.map((sortingKey) => {
129
+ if (typeof sortingKey === 'string') return {
130
+ key: sortingKey.substring(0, 1) === '-' ? sortingKey.substring(1) : sortingKey,
131
+ order: sortingKey.substring(0, 1) === '-' ? 'desc' : 'asc'
132
+ };
133
+ return sortingKey;
134
+ });
135
+ this._sortingKeys = this._sortingKeys.concat(sortingKeys);
124
136
  return this;
125
137
  }
126
138
 
@@ -277,6 +289,17 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
277
289
  }).concat(this._paginated ? `count(${this._table}.*) over() as total` : []).join(', ');
278
290
  }
279
291
 
292
+ _formatParameter(keyPath, value) {
293
+ let [table, key] = keyPath.split('.');
294
+ if (typeof key === 'undefined') [table, key] = [this._table, table];
295
+ switch ((tableInformation[caseit(table, this._options.casing.db)] || {})[caseit(key, this._options.casing.db)]) {
296
+ case 'jsonb':
297
+ return typeof value === 'string' ? value : JSON.stringify(value);
298
+ default:
299
+ return value;
300
+ }
301
+ }
302
+
280
303
  get _operatorMap() {
281
304
  return {
282
305
  '$or': 'or',
@@ -373,11 +396,11 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
373
396
  }
374
397
 
375
398
  if (!Array.isArray(condition[key])) {
376
- this._queryParameters.push(condition[key]);
399
+ this._queryParameters.push(this._formatParameter(key, condition[key]));
377
400
  return this._buildCondition(dbKey, comparer, `$${this._queryParameters.length}`);
378
401
  } else {
379
402
  const values = condition[key].map((value) => {
380
- this._queryParameters.push(value);
403
+ this._queryParameters.push(this._formatParameter(key, value));
381
404
  return `$${this._queryParameters.length}`;
382
405
  });
383
406
  return this._buildCondition(dbKey, comparer, `any(array[${values.join(',')}])`);
@@ -419,15 +442,24 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
419
442
  }
420
443
 
421
444
  _buildSorting() {
445
+
422
446
  if (!this._sortingKeys.length) return;
447
+
423
448
  const escapeIfNeeded = (value) => {
424
449
  if (value.substring(0, 1) == ':') return value.substring(1);
425
450
  return this._dbCase(value, true);
426
451
  };
427
- return `order by ${this._sortingKeys.map((key) => {
428
- if (key.substring(0, 1) == '-') return `${escapeIfNeeded(key.substring(1))} desc`;
429
- return escapeIfNeeded(key);
452
+
453
+ return `order by ${this._sortingKeys.map((sortingKey) => {
454
+ let condition = escapeIfNeeded(sortingKey.key);
455
+ if (Array.isArray(sortingKey.values)) {
456
+ condition = `case ${this._dbCase(sortingKey.key)} ${sortingKey.values.map((value, idx) => {
457
+ return `when '${value}' then ${idx}`;
458
+ }).join(' ')} end`;
459
+ }
460
+ return `${condition}${sortingKey.order === 'desc' ? ' desc' : ''}`;
430
461
  }).join(', ')}`;
462
+
431
463
  }
432
464
 
433
465
  _buildOffset() {
@@ -448,7 +480,7 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
448
480
  } else if (/^:/.test(value)) {
449
481
  value = value.substring(1);
450
482
  } else {
451
- this._queryParameters.push(value);
483
+ this._queryParameters.push(this._formatParameter(key, value));
452
484
  value = `$${this._queryParameters.length}`;
453
485
  }
454
486
  return `${this._dbCase(key, true)} = ${value}`;
@@ -460,8 +492,8 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
460
492
  }
461
493
 
462
494
  _buildInsertValues() {
463
- return this._insertValues.map((value) => {
464
- this._queryParameters.push(value);
495
+ return this._insertValues.map((value, idx) => {
496
+ this._queryParameters.push(this._formatParameter(this._insertKeys[idx], value));
465
497
  return `$${this._queryParameters.length}`;
466
498
  }).join(', ');
467
499
  }
@@ -547,8 +579,41 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
547
579
 
548
580
  }
549
581
 
582
+ async _resolveTableInformation() {
583
+ await tableInformationQueue.add(async () => {
584
+
585
+ const tables = [this._table]
586
+ .concat(this._joins.map((join) => join.table))
587
+ .filter((table) => !Object.keys(tableInformation).includes(table));
588
+
589
+ await Promise.all(tables.map(async (table) => {
590
+ const rows = await this._connection.exec(`SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '${table}';`, [], { format: 'raw' });
591
+ Object.assign(tableInformation, Object.fromEntries([[table, Object.fromEntries(rows.map((row) => {
592
+ return [row.column_name, row.data_type];
593
+ }))]]));
594
+ }));
595
+
596
+ });
597
+ }
598
+
550
599
  async _exec() {
551
- const rows = await this._executor(this);
600
+
601
+ await this._resolveTableInformation();
602
+
603
+ const [query, parameters] = this._build();
604
+
605
+ let rows = await this._connection.exec(
606
+ query,
607
+ parameters,
608
+ {
609
+ first: this._first,
610
+ transaction: this._transaction
611
+ });
612
+
613
+ if (['null', 'undefined'].includes(typeof rows)) {
614
+ rows = this._defaultResult;
615
+ }
616
+
552
617
  if (this._paginated && !this._first) {
553
618
  let total;
554
619
  if (rows.length == 0) {
@@ -563,7 +628,9 @@ module.exports = exports = class QueryBuilder extends CustomPromise {
563
628
  rows.forEach((item) => delete item.total);
564
629
  return { total, items: rows };
565
630
  }
631
+
566
632
  return rows;
633
+
567
634
  }
568
635
 
569
636
  then(resolve, reject) {