@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.
- package/lib/query-builder.js +85 -53
- package/package.json +1 -1
package/lib/query-builder.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
+
}
|