xcraft-core-pickaxe 0.1.6 → 0.1.9
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/operator-to-sql.js +9 -0
- package/lib/operators.js +25 -0
- package/lib/picks.js +39 -2
- package/lib/query-builder.js +56 -18
- package/lib/query-to-sql.js +18 -4
- package/package.json +2 -2
package/lib/operator-to-sql.js
CHANGED
|
@@ -166,6 +166,15 @@ const operators = {
|
|
|
166
166
|
)`;
|
|
167
167
|
},
|
|
168
168
|
|
|
169
|
+
query({query}, values) {
|
|
170
|
+
const {queryToSql} = require('./query-to-sql.js');
|
|
171
|
+
return `(${queryToSql(query, values).sql})`;
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
each({value}, values) {
|
|
175
|
+
return `json_each(${sql(value, values)})`;
|
|
176
|
+
},
|
|
177
|
+
|
|
169
178
|
eachValue(_, values) {
|
|
170
179
|
return 'json_each.value';
|
|
171
180
|
},
|
package/lib/operators.js
CHANGED
|
@@ -19,6 +19,10 @@ function op(value) {
|
|
|
19
19
|
if (type === 'object' && 'operator' in value) {
|
|
20
20
|
return value;
|
|
21
21
|
}
|
|
22
|
+
const {ValuePick} = require('./picks.js');
|
|
23
|
+
if (value instanceof ValuePick) {
|
|
24
|
+
return value.value;
|
|
25
|
+
}
|
|
22
26
|
|
|
23
27
|
throw new Error(`Bad value '${value}'`);
|
|
24
28
|
}
|
|
@@ -219,6 +223,20 @@ const operators = {
|
|
|
219
223
|
});
|
|
220
224
|
},
|
|
221
225
|
|
|
226
|
+
query(query) {
|
|
227
|
+
return /** @type {const} */ ({
|
|
228
|
+
operator: 'query',
|
|
229
|
+
query,
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
each(value) {
|
|
234
|
+
return /** @type {const} */ ({
|
|
235
|
+
operator: 'each',
|
|
236
|
+
value,
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
|
|
222
240
|
eachValue() {
|
|
223
241
|
return /** @type {const} */ ({
|
|
224
242
|
operator: 'eachValue',
|
|
@@ -342,4 +360,11 @@ const operators = {
|
|
|
342
360
|
* @typedef {Values<{[k in keyof typeof operators] : ReturnType<typeof operators[k]>}>} Operator
|
|
343
361
|
*/
|
|
344
362
|
|
|
363
|
+
/**
|
|
364
|
+
* @typedef {Operators["count"] | Operators["avg"] | Operators["max"] | Operators["min"] | Operators["sum"] | Operators["groupArray"]} Aggregator
|
|
365
|
+
*/
|
|
366
|
+
/**
|
|
367
|
+
* @typedef {Operators["abs"] | Operators["plus"] | Operators["minus"]} MathOperator
|
|
368
|
+
*/
|
|
369
|
+
|
|
345
370
|
module.exports = operators;
|
package/lib/picks.js
CHANGED
|
@@ -19,9 +19,8 @@ const $o = require('./operators.js');
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* @typedef {import('./operators.js').Operators} Operators
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
22
|
* @typedef {import('./operators.js').Operator} Operator
|
|
23
|
+
* @typedef {import('./operators.js').Aggregator} Aggregator
|
|
25
24
|
*/
|
|
26
25
|
|
|
27
26
|
/**
|
|
@@ -301,6 +300,27 @@ class RecordPick {
|
|
|
301
300
|
return $o.not(this.some((...args) => $o.not(func(...args))));
|
|
302
301
|
}
|
|
303
302
|
|
|
303
|
+
/**
|
|
304
|
+
* @param {(value: PickOf<V>, key: PickOf<K>) => Aggregator} fct
|
|
305
|
+
*/
|
|
306
|
+
select(fct) {
|
|
307
|
+
return $o.query({
|
|
308
|
+
from: $o.each(this.value),
|
|
309
|
+
select: [
|
|
310
|
+
fct(
|
|
311
|
+
makePick(this.#type.valuesType, {
|
|
312
|
+
field: $o.eachValue(),
|
|
313
|
+
path: [],
|
|
314
|
+
}),
|
|
315
|
+
makePick(this.#type.keysType, {
|
|
316
|
+
field: $o.eachKey(),
|
|
317
|
+
path: [],
|
|
318
|
+
})
|
|
319
|
+
),
|
|
320
|
+
],
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
304
324
|
/**
|
|
305
325
|
* @returns {ArrayPick<K>}
|
|
306
326
|
*/
|
|
@@ -398,6 +418,23 @@ class ArrayPick {
|
|
|
398
418
|
every(func) {
|
|
399
419
|
return $o.not(this.some((...args) => $o.not(func(...args))));
|
|
400
420
|
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @param {(value: PickOf<T>) => Aggregator} fct
|
|
424
|
+
*/
|
|
425
|
+
select(fct) {
|
|
426
|
+
return $o.query({
|
|
427
|
+
from: $o.each(this.value),
|
|
428
|
+
select: [
|
|
429
|
+
fct(
|
|
430
|
+
makePick(this.#type.valuesType, {
|
|
431
|
+
field: $o.eachValue(),
|
|
432
|
+
path: [],
|
|
433
|
+
})
|
|
434
|
+
),
|
|
435
|
+
],
|
|
436
|
+
});
|
|
437
|
+
}
|
|
401
438
|
}
|
|
402
439
|
|
|
403
440
|
/**
|
package/lib/query-builder.js
CHANGED
|
@@ -6,34 +6,21 @@ const {
|
|
|
6
6
|
toObjectType,
|
|
7
7
|
ObjectMapType,
|
|
8
8
|
RecordType,
|
|
9
|
+
object,
|
|
9
10
|
} = require('xcraft-core-stones');
|
|
10
11
|
const operators = require('./operators.js');
|
|
11
12
|
const {rowPick, ValuePick, RowPick, ObjectPick} = require('./picks.js');
|
|
12
13
|
const {queryToSql} = require('./query-to-sql.js');
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {import("./operators.js").Operator} Operator
|
|
15
|
-
*/
|
|
16
14
|
/**
|
|
17
15
|
* @typedef {import("./operators.js").Operators} Operators
|
|
16
|
+
* @typedef {import("./operators.js").Operator} Operator
|
|
17
|
+
* @typedef {import("./operators.js").Aggregator} Aggregator
|
|
18
|
+
* @typedef {import("./operators.js").MathOperator} MathOperator
|
|
18
19
|
*/
|
|
19
20
|
|
|
20
|
-
/**
|
|
21
|
-
* @template {AnyTypeOrShape} T
|
|
22
|
-
* @typedef {import("./picks.js").PickOf<T>} PickOf
|
|
23
|
-
*/
|
|
24
21
|
/**
|
|
25
22
|
* @typedef {import("./picks.js").AnyPick} AnyPick
|
|
26
23
|
*/
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {import("./picks.js").Path} Path
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @typedef {Operators["count"] | Operators["avg"] | Operators["max"] | Operators["min"] | Operators["sum"] | Operators["groupArray"]} Aggregator
|
|
33
|
-
*/
|
|
34
|
-
/**
|
|
35
|
-
* @typedef {Operators["abs"] | Operators["plus"] | Operators["minus"]} MathOperator
|
|
36
|
-
*/
|
|
37
24
|
|
|
38
25
|
/**
|
|
39
26
|
* @typedef {AnyPick | Aggregator | MathOperator} SelectValue
|
|
@@ -72,6 +59,8 @@ const {queryToSql} = require('./query-to-sql.js');
|
|
|
72
59
|
* where?: Operator,
|
|
73
60
|
* orderBy?: OrderByResult,
|
|
74
61
|
* groupBy?: GroupByResult
|
|
62
|
+
* limit?: number
|
|
63
|
+
* offset?: number
|
|
75
64
|
* }} QueryObj
|
|
76
65
|
*/
|
|
77
66
|
|
|
@@ -127,6 +116,7 @@ class FinalQuery {
|
|
|
127
116
|
if (
|
|
128
117
|
type instanceof ArrayType ||
|
|
129
118
|
type instanceof ObjectType ||
|
|
119
|
+
type === object ||
|
|
130
120
|
type instanceof ObjectMapType ||
|
|
131
121
|
type instanceof RecordType
|
|
132
122
|
) {
|
|
@@ -188,7 +178,7 @@ class FinalQuery {
|
|
|
188
178
|
}
|
|
189
179
|
|
|
190
180
|
sql() {
|
|
191
|
-
return queryToSql(this.#queryParts,
|
|
181
|
+
return queryToSql(this.#queryParts, null).sql;
|
|
192
182
|
}
|
|
193
183
|
|
|
194
184
|
/**
|
|
@@ -308,6 +298,30 @@ class ScopedSelectQuery extends FinalQuery {
|
|
|
308
298
|
};
|
|
309
299
|
return new ScopedSelectQuery(this.#database, this.#type, queryParts);
|
|
310
300
|
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @param {number} count
|
|
304
|
+
* @returns {ScopedSelectQuery<T,R>}
|
|
305
|
+
*/
|
|
306
|
+
limit(count) {
|
|
307
|
+
const queryParts = {
|
|
308
|
+
...this.#queryParts,
|
|
309
|
+
limit: count,
|
|
310
|
+
};
|
|
311
|
+
return new ScopedSelectQuery(this.#database, this.#type, queryParts);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @param {number} count
|
|
316
|
+
* @returns {ScopedSelectQuery<T,R>}
|
|
317
|
+
*/
|
|
318
|
+
offset(count) {
|
|
319
|
+
const queryParts = {
|
|
320
|
+
...this.#queryParts,
|
|
321
|
+
offset: count,
|
|
322
|
+
};
|
|
323
|
+
return new ScopedSelectQuery(this.#database, this.#type, queryParts);
|
|
324
|
+
}
|
|
311
325
|
}
|
|
312
326
|
|
|
313
327
|
/**
|
|
@@ -381,6 +395,30 @@ class SelectQuery extends FinalQuery {
|
|
|
381
395
|
};
|
|
382
396
|
return new SelectQuery(this.#database, this.#type, queryParts);
|
|
383
397
|
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* @param {number} count
|
|
401
|
+
* @returns {SelectQuery<T,R>}
|
|
402
|
+
*/
|
|
403
|
+
limit(count) {
|
|
404
|
+
const queryParts = {
|
|
405
|
+
...this.#queryParts,
|
|
406
|
+
limit: count,
|
|
407
|
+
};
|
|
408
|
+
return new SelectQuery(this.#database, this.#type, queryParts);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @param {number} count
|
|
413
|
+
* @returns {SelectQuery<T,R>}
|
|
414
|
+
*/
|
|
415
|
+
offset(count) {
|
|
416
|
+
const queryParts = {
|
|
417
|
+
...this.#queryParts,
|
|
418
|
+
offset: count,
|
|
419
|
+
};
|
|
420
|
+
return new SelectQuery(this.#database, this.#type, queryParts);
|
|
421
|
+
}
|
|
384
422
|
}
|
|
385
423
|
|
|
386
424
|
/**
|
package/lib/query-to-sql.js
CHANGED
|
@@ -45,14 +45,16 @@ function groupByFields(groupBy, values) {
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* @param {QueryObj} query
|
|
48
|
-
* @param {
|
|
48
|
+
* @param {any[] | null} [values]
|
|
49
49
|
* @returns {{sql: string, values: any[] | null}}
|
|
50
50
|
*/
|
|
51
|
-
function queryToSql(query,
|
|
52
|
-
const values = useBindedValues ? [] : null;
|
|
51
|
+
function queryToSql(query, values = []) {
|
|
53
52
|
const distinct = query.distinct ? 'DISTINCT ' : '';
|
|
53
|
+
// Note: query.from is not validated
|
|
54
|
+
const from =
|
|
55
|
+
typeof query.from === 'string' ? query.from : sql(query.from, values);
|
|
54
56
|
let result = `SELECT ${distinct}${selectFields(query.select, values)}`;
|
|
55
|
-
result += '\n' + `FROM ${
|
|
57
|
+
result += '\n' + `FROM ${from}`;
|
|
56
58
|
if (query.where) {
|
|
57
59
|
result += '\n' + `WHERE ${sql(query.where, values)}`;
|
|
58
60
|
}
|
|
@@ -62,6 +64,18 @@ function queryToSql(query, useBindedValues = true) {
|
|
|
62
64
|
if (query.groupBy) {
|
|
63
65
|
result += '\n' + `GROUP BY ${groupByFields(query.groupBy, values)}`;
|
|
64
66
|
}
|
|
67
|
+
if (query.limit) {
|
|
68
|
+
if (!Number.isInteger(query.limit)) {
|
|
69
|
+
throw new Error(`Bad limit '${query.limit}'`);
|
|
70
|
+
}
|
|
71
|
+
result += '\n' + `LIMIT ${query.limit}`;
|
|
72
|
+
}
|
|
73
|
+
if (query.offset) {
|
|
74
|
+
if (!Number.isInteger(query.offset)) {
|
|
75
|
+
throw new Error(`Bad offset '${query.offset}'`);
|
|
76
|
+
}
|
|
77
|
+
result += '\n' + `OFFSET ${query.offset}`;
|
|
78
|
+
}
|
|
65
79
|
return {sql: result, values};
|
|
66
80
|
}
|
|
67
81
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xcraft-core-pickaxe",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Query builder",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"prettier": "2.0.4",
|
|
24
24
|
"xcraft-dev-prettier": "^2.0.0",
|
|
25
|
-
"xcraft-dev-rules": "^4.
|
|
25
|
+
"xcraft-dev-rules": "^4.3.0"
|
|
26
26
|
},
|
|
27
27
|
"prettier": "xcraft-dev-prettier"
|
|
28
28
|
}
|