@trenskow/pged 5.1.37 → 5.1.39

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.
@@ -50,6 +50,25 @@ export default class QueryBuilder extends CustomPromise {
50
50
 
51
51
  }
52
52
 
53
+ get _transforms() {
54
+ return {
55
+ function: ({ name, parameters }) => {
56
+ return `${name}(${parameters.map((parameter) => this._resolveConstruct(parameter)).join(', ')})`;
57
+ }
58
+ };
59
+ }
60
+
61
+ _resolveConstruct(construct, transforms) {
62
+ if (typeof construct === 'string') {
63
+ if (construct[0] === ':') return this._dbCase(construct.substring(1));
64
+ else {
65
+ this._queryParameters = (this._queryParameters || []).concat([construct]);
66
+ return `$${this._queryParameters.length}`;
67
+ }
68
+ }
69
+ return (transforms || this._transforms)[construct.type](construct);
70
+ }
71
+
53
72
  _dbCasePart(input, quote) {
54
73
  return input
55
74
  .split(/"|'/)
@@ -101,7 +120,23 @@ export default class QueryBuilder extends CustomPromise {
101
120
  }
102
121
 
103
122
  select(keys = ['*']) {
104
- if (typeof keys !== 'string' || keys[0] !== ':') {
123
+ if (typeof keys === 'function') {
124
+ keys = keys({
125
+ rank: ({ column, language, query }) => ({
126
+ type: 'alias',
127
+ name: 'rank',
128
+ value: {
129
+ type: 'function',
130
+ name: 'ts_rank',
131
+ parameters: [ column, {
132
+ type: 'function',
133
+ name: 'websearch_to_tsquery',
134
+ parameters: (language ? [language] : []).concat([query])
135
+ } ]
136
+ }
137
+ })
138
+ });
139
+ } else if (typeof keys !== 'string' || keys[0] !== ':') {
105
140
  if (!Array.isArray(keys)) {
106
141
  keys = [].concat(...keys.split('"').map((key, idx) => {
107
142
  if (idx % 2 == 0) return key.split(/, ?/);
@@ -113,6 +148,11 @@ export default class QueryBuilder extends CustomPromise {
113
148
  return this;
114
149
  }
115
150
 
151
+ distinct(column) {
152
+ this._distinct = column || true;
153
+ return this;
154
+ }
155
+
116
156
  groupBy(element) {
117
157
  this._groupBy = element;
118
158
  return this;
@@ -202,6 +242,7 @@ export default class QueryBuilder extends CustomPromise {
202
242
  }));
203
243
  } else {
204
244
  if (typeof conditions !== 'object') throw new TypeError('Conditions must be an object.');
245
+ else if (['comparison'].includes(conditions?.type)) return conditions;
205
246
  return Object.keys(conditions).map((key) => {
206
247
  let obj = {};
207
248
  const dbKey = this._dbCase(key);
@@ -218,6 +259,20 @@ export default class QueryBuilder extends CustomPromise {
218
259
  }
219
260
 
220
261
  where(conditions) {
262
+ if (typeof conditions === 'function') {
263
+ conditions = conditions({
264
+ search: ({ column, language, query }) => ({
265
+ type: 'comparison',
266
+ lhs: column,
267
+ operator: '@@',
268
+ rhs: {
269
+ type: 'function',
270
+ name: 'websearch_to_tsquery',
271
+ parameters: (language ? [language] : []).concat([query])
272
+ }
273
+ })
274
+ });
275
+ }
221
276
  this._conditions = this._conditions.concat(this._formalizeConditions(conditions));
222
277
  return this;
223
278
  }
@@ -307,45 +362,6 @@ export default class QueryBuilder extends CustomPromise {
307
362
  return this;
308
363
  }
309
364
 
310
- search({ column, query, language, type = 'web' }) {
311
-
312
- if (language?.[0] === ':') language = this._dbCase(language.substring(1));
313
- else {
314
- this._queryParameters = (this._queryParameters || []).concat([language]);
315
- language = `$${this._queryParameters.length}`;
316
- }
317
-
318
- this._queryParameters = (this._queryParameters || []).concat([query]);
319
- query = `$${this._queryParameters.length}`;
320
-
321
- const vectorConverter = type === 'web' ? 'websearch_to_tsquery' : 'to_tsquery';
322
-
323
- let vectorConverterArgs = [query];
324
-
325
- if (language) {
326
- vectorConverterArgs.unshift(language);
327
- }
328
-
329
- column = this._dbCase(column);
330
-
331
- this._selectKeys = (this._selectKeys || []).concat([
332
- `ts_rank(${column}, ${vectorConverter}(${vectorConverterArgs.join(', ')})) as rank`
333
- ]);
334
-
335
- this._sortingKeys = this._sortingKeys.concat([{
336
- key: 'rank',
337
- order: 'desc'
338
- }]);
339
-
340
- return this
341
- .where({
342
- $fullText: {
343
- [`:${column}`]: `:${vectorConverter}(${vectorConverterArgs.join(', ')})`
344
- }
345
- });
346
-
347
- }
348
-
349
365
  _canQuote(key) {
350
366
  if (key === '*') return false;
351
367
  if (key.toLowerCase().includes(' as ')) return false;
@@ -354,12 +370,15 @@ export default class QueryBuilder extends CustomPromise {
354
370
  }
355
371
 
356
372
  _buildKeys(keys = ['*'], quote) {
357
- return keys.map((key) => {
373
+ return (this._distinct ? [this._distinct === true ? 'distinct' : `distinct on (${this._dbCase(this._distinct)})`] : []).concat(keys.map((key) => {
374
+ if (typeof key === 'object' && key !== null && !Array.isArray(key)) return this._resolveConstruct(key, {
375
+ alias: ({ name, value }) => `${this._resolveConstruct(value)} as ${name}`
376
+ });
358
377
  if (key.substring(0, 1) == ':') return key.substring(1);
359
378
  let as = key.split(':');
360
379
  if (as.length == 1) return this._dbCase(as[0], quote && this._canQuote(key));
361
380
  return `${this._dbCase(as[0], quote)} as ${this._dbCase(as[1])}`;
362
- }).concat(this._paginated ? `count(${this._table}.*) over() as total` : []).join(', ');
381
+ }).concat(this._paginated ? `count(${this._table}.*) over() as total` : []).join(', ')).join(' ');
363
382
  }
364
383
 
365
384
  _formatParameter(keyPath, value) {
@@ -417,6 +436,14 @@ export default class QueryBuilder extends CustomPromise {
417
436
 
418
437
  const result = conditions.map((condition) => {
419
438
 
439
+ if (['comparison'].includes(condition?.type)) {
440
+ return this._resolveConstruct(condition, {
441
+ comparison: ({ lhs, operator, rhs }) => {
442
+ return `${this._resolveConstruct(lhs)} ${operator} ${this._resolveConstruct(rhs)}`;
443
+ }
444
+ });
445
+ }
446
+
420
447
  let key = Object.keys(condition)[0];
421
448
 
422
449
  if (key.substring(0, 1) == '$') {
@@ -680,7 +707,7 @@ export default class QueryBuilder extends CustomPromise {
680
707
  .filter((table) => !Object.keys(tableInformation).includes(table));
681
708
 
682
709
  await Promise.all(tables.map(async (table) => {
683
- const rows = await this._connection.exec(`SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '${table}';`, [], { format: 'raw' });
710
+ const rows = await this._connection.exec(`SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '${this._dbCase(table)}';`, [], { format: 'raw' });
684
711
  Object.assign(tableInformation, Object.fromEntries([[table, Object.fromEntries(rows.map((row) => {
685
712
  return [row.column_name, row.data_type];
686
713
  }))]]));
@@ -693,6 +720,17 @@ export default class QueryBuilder extends CustomPromise {
693
720
 
694
721
  await this._resolveTableInformation();
695
722
 
723
+ if (this._paginated && !this._first && this._limit === 0) {
724
+ delete this._paginated;
725
+ delete this._offset;
726
+ delete this._limit;
727
+ this._sortingKeys = [];
728
+ return {
729
+ total: parseInt(await this.count('*')._exec()),
730
+ items: []
731
+ };
732
+ }
733
+
696
734
  const [query, parameters] = this._build();
697
735
 
698
736
  let rows = await this._connection.exec(
@@ -708,14 +746,8 @@ export default class QueryBuilder extends CustomPromise {
708
746
  }
709
747
 
710
748
  if (this._paginated && !this._first) {
711
- let total;
712
- if (rows.length == 0) {
713
- delete this._paginated;
714
- delete this._offset;
715
- delete this._limit;
716
- this._sortingKeys = [];
717
- total = parseInt(await this.count('*')._exec());
718
- } else {
749
+ let total = 0;
750
+ if (rows.length > 0) {
719
751
  total = parseInt(((rows || [])[0] || {})['total'] || 0);
720
752
  }
721
753
  rows.forEach((item) => delete item.total);
@@ -733,8 +765,8 @@ export default class QueryBuilder extends CustomPromise {
733
765
  .catch((error) => this._reject(error));
734
766
 
735
767
  return super
736
- .then(resolve, reject)
768
+ .then(resolve, reject);
737
769
 
738
770
  }
739
771
 
740
- }
772
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trenskow/pged",
3
- "version": "5.1.37",
3
+ "version": "5.1.39",
4
4
  "description": "Just a silly little db management and query builder for PostgreSQL.",
5
5
  "main": "index.js",
6
6
  "type": "module",