xcraft-core-pickaxe 0.1.19 → 0.1.22
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/examples.js +0 -3
- package/lib/join-operators.js +21 -0
- package/lib/operator-to-sql.js +21 -3
- package/lib/operators.js +27 -1
- package/lib/picks.js +115 -29
- package/lib/query-builder.js +269 -238
- package/lib/query-to-sql.js +59 -3
- package/package.json +1 -1
- package/test/sql.spec.js +348 -8
package/lib/examples.js
CHANGED
|
@@ -87,7 +87,6 @@ example1: {
|
|
|
87
87
|
example2: {
|
|
88
88
|
const userIds = ['user@toto', 'user@tata'];
|
|
89
89
|
const builder = new QueryBuilder()
|
|
90
|
-
.db('test_db')
|
|
91
90
|
.from('test_table', TestUserShape3)
|
|
92
91
|
.fields(['firstname', 'age'])
|
|
93
92
|
.where((user, $) =>
|
|
@@ -104,7 +103,6 @@ example2: {
|
|
|
104
103
|
|
|
105
104
|
example3: {
|
|
106
105
|
const builder = new QueryBuilder()
|
|
107
|
-
.db('test_db')
|
|
108
106
|
.from('test_table', ActionShape(TestUserShape))
|
|
109
107
|
.scope((row) => row.field('action').get('payload').get('state'))
|
|
110
108
|
.fields(['firstname', 'age']);
|
|
@@ -119,7 +117,6 @@ example3: {
|
|
|
119
117
|
*/
|
|
120
118
|
function queryAction(shape) {
|
|
121
119
|
const builder = new QueryBuilder()
|
|
122
|
-
.db('test_db')
|
|
123
120
|
.from('test_table', ActionShape(shape))
|
|
124
121
|
.scope((row) => row.field('action').get('payload').get('state'));
|
|
125
122
|
return /** @type {ScopedFromQuery<GetShape<T>>} */ (builder);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const joinOperators = /** @type {const} */ ([
|
|
2
|
+
'join',
|
|
3
|
+
'left join',
|
|
4
|
+
'right join',
|
|
5
|
+
'full join',
|
|
6
|
+
'left outer join',
|
|
7
|
+
'right outer join',
|
|
8
|
+
'full outer join',
|
|
9
|
+
'inner join',
|
|
10
|
+
'cross join',
|
|
11
|
+
'natural join',
|
|
12
|
+
'natural left join',
|
|
13
|
+
'natural right join',
|
|
14
|
+
'natural full join',
|
|
15
|
+
'natural left outer join',
|
|
16
|
+
'natural right outer join',
|
|
17
|
+
'natural full outer join',
|
|
18
|
+
'natural inner join',
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
module.exports = {joinOperators};
|
package/lib/operator-to-sql.js
CHANGED
|
@@ -32,9 +32,12 @@ const operators = {
|
|
|
32
32
|
return 'NULL';
|
|
33
33
|
},
|
|
34
34
|
|
|
35
|
-
field({field}, context) {
|
|
35
|
+
field({tableName, field}, context) {
|
|
36
36
|
// Note: field is not validated
|
|
37
37
|
if (typeof field === 'string') {
|
|
38
|
+
if (context.useTableNames && tableName) {
|
|
39
|
+
return `${tableName}.${field}`;
|
|
40
|
+
}
|
|
38
41
|
return `${field}`;
|
|
39
42
|
}
|
|
40
43
|
return sql(field, context);
|
|
@@ -78,12 +81,25 @@ const operators = {
|
|
|
78
81
|
.join(' || ')})`;
|
|
79
82
|
},
|
|
80
83
|
|
|
84
|
+
stringLength({value}, context) {
|
|
85
|
+
return `LENGTH(${sql(value, context)})`;
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
substr({value, start, length}, context) {
|
|
89
|
+
const valueSql = sql(value, context);
|
|
90
|
+
const startSql = sql(start, context);
|
|
91
|
+
const lengthSql = length !== undefined ? `, ${sql(length, context)}` : '';
|
|
92
|
+
return `SUBSTR(${valueSql}, ${startSql}${lengthSql})`;
|
|
93
|
+
},
|
|
94
|
+
|
|
81
95
|
eq({a, b}, context) {
|
|
82
|
-
|
|
96
|
+
const operator = context.equalOperator === '=' ? '=' : 'IS';
|
|
97
|
+
return `${sql(a, context)} ${operator} ${sql(b, context)}`;
|
|
83
98
|
},
|
|
84
99
|
|
|
85
100
|
neq({a, b}, context) {
|
|
86
|
-
|
|
101
|
+
const operator = context.equalOperator === '=' ? '<>' : 'IS NOT';
|
|
102
|
+
return `${sql(a, context)} ${operator} ${sql(b, context)}`;
|
|
87
103
|
},
|
|
88
104
|
|
|
89
105
|
gte({a, b}, context) {
|
|
@@ -264,6 +280,8 @@ function getOperator(operatorOrPick) {
|
|
|
264
280
|
* @typedef {{
|
|
265
281
|
* values: any[] | null,
|
|
266
282
|
* scope?: any,
|
|
283
|
+
* useTableNames?: boolean,
|
|
284
|
+
* equalOperator: 'IS' | '=',
|
|
267
285
|
* }} OperatorToSqlContext
|
|
268
286
|
*/
|
|
269
287
|
|
package/lib/operators.js
CHANGED
|
@@ -41,9 +41,10 @@ const operators = {
|
|
|
41
41
|
});
|
|
42
42
|
},
|
|
43
43
|
|
|
44
|
-
field(field) {
|
|
44
|
+
field(field, tableName) {
|
|
45
45
|
return /** @type {const} */ ({
|
|
46
46
|
operator: 'field',
|
|
47
|
+
tableName,
|
|
47
48
|
field,
|
|
48
49
|
});
|
|
49
50
|
},
|
|
@@ -79,6 +80,28 @@ const operators = {
|
|
|
79
80
|
});
|
|
80
81
|
},
|
|
81
82
|
|
|
83
|
+
stringLength(value) {
|
|
84
|
+
value = op(value);
|
|
85
|
+
return /** @type {const} */ ({
|
|
86
|
+
operator: 'stringLength',
|
|
87
|
+
value,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
substr(value, start, length) {
|
|
92
|
+
value = op(value);
|
|
93
|
+
start = op(start);
|
|
94
|
+
if (length !== undefined) {
|
|
95
|
+
length = op(length);
|
|
96
|
+
}
|
|
97
|
+
return /** @type {const} */ ({
|
|
98
|
+
operator: 'substr',
|
|
99
|
+
value,
|
|
100
|
+
start,
|
|
101
|
+
length,
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
|
|
82
105
|
eq(a, b) {
|
|
83
106
|
a = op(a);
|
|
84
107
|
b = op(b);
|
|
@@ -383,6 +406,9 @@ const operators = {
|
|
|
383
406
|
/**
|
|
384
407
|
* @typedef {Operators["count"] | Operators["avg"] | Operators["max"] | Operators["min"] | Operators["sum"] | Operators["groupArray"]} Aggregator
|
|
385
408
|
*/
|
|
409
|
+
/**
|
|
410
|
+
* @typedef {Operators["eq"] | Operators["neq"] | Operators["and"] | Operators["or"] | Operators["not"] | Operators["gt"] | Operators["gte"] | Operators["lt"] | Operators["lte"] | Operators["in"] | Operators["includes"] | Operators["some"] | Operators["glob"] | Operators["like"] | Operators["match"]} BooleanOperator
|
|
411
|
+
*/
|
|
386
412
|
/**
|
|
387
413
|
* @typedef {Operators["abs"] | Operators["plus"] | Operators["minus"]} MathOperator
|
|
388
414
|
*/
|
package/lib/picks.js
CHANGED
|
@@ -13,6 +13,10 @@ const {
|
|
|
13
13
|
StringType,
|
|
14
14
|
string,
|
|
15
15
|
NumberType,
|
|
16
|
+
EnumerationType,
|
|
17
|
+
toObjectType,
|
|
18
|
+
boolean,
|
|
19
|
+
BooleanType,
|
|
16
20
|
} = require('xcraft-core-stones');
|
|
17
21
|
|
|
18
22
|
const $o = require('./operators.js');
|
|
@@ -21,6 +25,7 @@ const $o = require('./operators.js');
|
|
|
21
25
|
* @typedef {import('./operators.js').Operators} Operators
|
|
22
26
|
* @typedef {import('./operators.js').Operator} Operator
|
|
23
27
|
* @typedef {import('./operators.js').Aggregator} Aggregator
|
|
28
|
+
* @typedef {import("./operators.js").BooleanOperator} BooleanOperator
|
|
24
29
|
*/
|
|
25
30
|
|
|
26
31
|
/**
|
|
@@ -30,6 +35,10 @@ const $o = require('./operators.js');
|
|
|
30
35
|
* @typedef {PathElement[]} Path
|
|
31
36
|
*/
|
|
32
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {ValuePick<BooleanType> | BooleanOperator} BooleanValue
|
|
40
|
+
*/
|
|
41
|
+
|
|
33
42
|
/**
|
|
34
43
|
* @template T
|
|
35
44
|
* @template {keyof T} K
|
|
@@ -39,7 +48,8 @@ const $o = require('./operators.js');
|
|
|
39
48
|
/**
|
|
40
49
|
* @typedef {{
|
|
41
50
|
* field: (keyof any) | SelectValues<Operators, "eachValue" | "eachKey" | "keys" | "values" | "length">,
|
|
42
|
-
* path: Path
|
|
51
|
+
* path: Path,
|
|
52
|
+
* tableName?: string
|
|
43
53
|
* }} Context
|
|
44
54
|
*/
|
|
45
55
|
|
|
@@ -49,7 +59,7 @@ const $o = require('./operators.js');
|
|
|
49
59
|
|
|
50
60
|
/**
|
|
51
61
|
* @template {Type} T
|
|
52
|
-
* @typedef { [T] extends [ArrayType<infer V>] ? ArrayPick<V> : [T] extends [ObjectType<infer S>] ? ObjectPick<S> : [T] extends [ObjectMapType<infer V>] ? RecordPick<StringType,V> : [T] extends [RecordType<infer K,infer V>] ? RecordPick<K,V> : [T] extends [NumberType] ? NumberPick : ValuePick<T>} PickOfType
|
|
62
|
+
* @typedef { [T] extends [ArrayType<infer V>] ? ArrayPick<V> : [T] extends [ObjectType<infer S>] ? ObjectPick<S> : [T] extends [ObjectMapType<infer V>] ? RecordPick<StringType,V> : [T] extends [RecordType<infer K,infer V>] ? RecordPick<K,V> : [T] extends [NumberType] ? NumberPick : [T] extends [StringType] ? StringPick : ValuePick<T>} PickOfType
|
|
53
63
|
*/
|
|
54
64
|
/**
|
|
55
65
|
* @template {AnyTypeOrShape} T
|
|
@@ -87,22 +97,33 @@ class ValuePick {
|
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
get value() {
|
|
90
|
-
const {field, path} = this.#context;
|
|
100
|
+
const {tableName, field, path} = this.#context;
|
|
91
101
|
if (path.length > 0) {
|
|
92
|
-
return $o.get($o.field(field), path);
|
|
102
|
+
return $o.get($o.field(field, tableName), path);
|
|
93
103
|
}
|
|
94
|
-
return $o.field(field);
|
|
104
|
+
return $o.field(field, tableName);
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
get type() {
|
|
98
108
|
return this.#type;
|
|
99
109
|
}
|
|
100
110
|
|
|
111
|
+
/**
|
|
112
|
+
* @template {AnyObjectShape} T
|
|
113
|
+
* @param {T} shape
|
|
114
|
+
*/
|
|
115
|
+
asObject(shape) {
|
|
116
|
+
return new ObjectPick(toObjectType(shape), this.#context);
|
|
117
|
+
}
|
|
118
|
+
|
|
101
119
|
/**
|
|
102
120
|
* @param {t<T> | ValuePick<T>} value
|
|
103
121
|
*/
|
|
104
122
|
eq(value) {
|
|
105
|
-
return
|
|
123
|
+
return new ValuePick(boolean, {
|
|
124
|
+
field: $o.eq(this.value, value),
|
|
125
|
+
path: [],
|
|
126
|
+
});
|
|
106
127
|
}
|
|
107
128
|
|
|
108
129
|
/**
|
|
@@ -147,6 +168,18 @@ class ValuePick {
|
|
|
147
168
|
return $o.in(this.value, list);
|
|
148
169
|
}
|
|
149
170
|
|
|
171
|
+
/**
|
|
172
|
+
* @param {any} value
|
|
173
|
+
*/
|
|
174
|
+
match(value) {
|
|
175
|
+
return $o.match(this.value, value);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @extends {ValuePick<StringType>}
|
|
181
|
+
*/
|
|
182
|
+
class StringPick extends ValuePick {
|
|
150
183
|
/**
|
|
151
184
|
* @param {string | ValuePick<StringType>} value
|
|
152
185
|
*/
|
|
@@ -161,11 +194,22 @@ class ValuePick {
|
|
|
161
194
|
return $o.glob(this.value, value);
|
|
162
195
|
}
|
|
163
196
|
|
|
197
|
+
get length() {
|
|
198
|
+
return new NumberPick(number, {
|
|
199
|
+
field: $o.stringLength(this.value),
|
|
200
|
+
path: [],
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
164
204
|
/**
|
|
165
|
-
* @param {
|
|
205
|
+
* @param {number | ValuePick<NumberType>} start
|
|
206
|
+
* @param {number | ValuePick<NumberType>} [length]
|
|
166
207
|
*/
|
|
167
|
-
|
|
168
|
-
return
|
|
208
|
+
substr(start, length) {
|
|
209
|
+
return new StringPick(string, {
|
|
210
|
+
field: $o.substr(this.value, start, length),
|
|
211
|
+
path: [],
|
|
212
|
+
});
|
|
169
213
|
}
|
|
170
214
|
}
|
|
171
215
|
|
|
@@ -174,21 +218,30 @@ class ValuePick {
|
|
|
174
218
|
*/
|
|
175
219
|
class NumberPick extends ValuePick {
|
|
176
220
|
abs() {
|
|
177
|
-
return
|
|
221
|
+
return new NumberPick(number, {
|
|
222
|
+
field: $o.abs(this.value),
|
|
223
|
+
path: [],
|
|
224
|
+
});
|
|
178
225
|
}
|
|
179
226
|
|
|
180
227
|
/**
|
|
181
228
|
* @param {number | ValuePick<NumberType>} value
|
|
182
229
|
*/
|
|
183
230
|
plus(value) {
|
|
184
|
-
return
|
|
231
|
+
return new NumberPick(number, {
|
|
232
|
+
field: $o.plus(this.value, value),
|
|
233
|
+
path: [],
|
|
234
|
+
});
|
|
185
235
|
}
|
|
186
236
|
|
|
187
237
|
/**
|
|
188
238
|
* @param {number | ValuePick<NumberType>} value
|
|
189
239
|
*/
|
|
190
240
|
minus(value) {
|
|
191
|
-
return
|
|
241
|
+
return new NumberPick(number, {
|
|
242
|
+
field: $o.minus(this.value, value),
|
|
243
|
+
path: [],
|
|
244
|
+
});
|
|
192
245
|
}
|
|
193
246
|
}
|
|
194
247
|
|
|
@@ -211,17 +264,21 @@ class ObjectPick {
|
|
|
211
264
|
}
|
|
212
265
|
|
|
213
266
|
get value() {
|
|
214
|
-
const {field, path} = this.#context;
|
|
267
|
+
const {tableName, field, path} = this.#context;
|
|
215
268
|
if (path.length > 0) {
|
|
216
|
-
return $o.get($o.field(field), path);
|
|
269
|
+
return $o.get($o.field(field, tableName), path);
|
|
217
270
|
}
|
|
218
|
-
return $o.field(field);
|
|
271
|
+
return $o.field(field, tableName);
|
|
219
272
|
}
|
|
220
273
|
|
|
221
274
|
get type() {
|
|
222
275
|
return this.#type;
|
|
223
276
|
}
|
|
224
277
|
|
|
278
|
+
toRowPick() {
|
|
279
|
+
return new RowPick(this.#type, this.#context);
|
|
280
|
+
}
|
|
281
|
+
|
|
225
282
|
/**
|
|
226
283
|
* @template {keyof T} K
|
|
227
284
|
* @param {K} key
|
|
@@ -262,11 +319,11 @@ class RecordPick {
|
|
|
262
319
|
}
|
|
263
320
|
|
|
264
321
|
get value() {
|
|
265
|
-
const {field, path} = this.#context;
|
|
322
|
+
const {tableName, field, path} = this.#context;
|
|
266
323
|
if (path.length > 0) {
|
|
267
|
-
return $o.get($o.field(field), path);
|
|
324
|
+
return $o.get($o.field(field, tableName), path);
|
|
268
325
|
}
|
|
269
|
-
return $o.field(field);
|
|
326
|
+
return $o.field(field, tableName);
|
|
270
327
|
}
|
|
271
328
|
|
|
272
329
|
get type() {
|
|
@@ -375,11 +432,11 @@ class ArrayPick {
|
|
|
375
432
|
}
|
|
376
433
|
|
|
377
434
|
get value() {
|
|
378
|
-
const {field, path} = this.#context;
|
|
435
|
+
const {tableName, field, path} = this.#context;
|
|
379
436
|
if (path.length > 0) {
|
|
380
|
-
return $o.get($o.field(field), path);
|
|
437
|
+
return $o.get($o.field(field, tableName), path);
|
|
381
438
|
}
|
|
382
|
-
return $o.field(field);
|
|
439
|
+
return $o.field(field, tableName);
|
|
383
440
|
}
|
|
384
441
|
|
|
385
442
|
get type() {
|
|
@@ -412,7 +469,7 @@ class ArrayPick {
|
|
|
412
469
|
}
|
|
413
470
|
|
|
414
471
|
/**
|
|
415
|
-
* @param {(value: PickOf<T>) =>
|
|
472
|
+
* @param {(value: PickOf<T>) => BooleanValue} func
|
|
416
473
|
*/
|
|
417
474
|
some(func) {
|
|
418
475
|
return $o.some(
|
|
@@ -470,12 +527,16 @@ function isAnyPick(value) {
|
|
|
470
527
|
class RowPick {
|
|
471
528
|
/** @type {ObjectType<T>} */
|
|
472
529
|
#type;
|
|
530
|
+
/** @type {Partial<Context>} */
|
|
531
|
+
#context;
|
|
473
532
|
|
|
474
533
|
/**
|
|
475
534
|
* @param {ObjectType<T>} type
|
|
535
|
+
* @param {Partial<Context>} context
|
|
476
536
|
*/
|
|
477
|
-
constructor(type) {
|
|
537
|
+
constructor(type, context) {
|
|
478
538
|
this.#type = type;
|
|
539
|
+
this.#context = context;
|
|
479
540
|
}
|
|
480
541
|
|
|
481
542
|
/**
|
|
@@ -484,12 +545,29 @@ class RowPick {
|
|
|
484
545
|
* @returns {PickOf<T[K]>}
|
|
485
546
|
*/
|
|
486
547
|
field(fieldName) {
|
|
487
|
-
const context =
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
548
|
+
const context = this.#context.field
|
|
549
|
+
? {
|
|
550
|
+
...this.#context,
|
|
551
|
+
field: this.#context.field,
|
|
552
|
+
path: [...(this.#context.path ?? []), fieldName],
|
|
553
|
+
}
|
|
554
|
+
: {
|
|
555
|
+
...this.#context,
|
|
556
|
+
field: fieldName,
|
|
557
|
+
path: [],
|
|
558
|
+
};
|
|
491
559
|
return makePick(this.#type.properties[fieldName], context);
|
|
492
560
|
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Alias of field method to be symmetric with ObjectPick
|
|
564
|
+
* @template {keyof T} K
|
|
565
|
+
* @param {K} fieldName
|
|
566
|
+
* @returns {PickOf<T[K]>}
|
|
567
|
+
*/
|
|
568
|
+
get(fieldName) {
|
|
569
|
+
return this.field(fieldName);
|
|
570
|
+
}
|
|
493
571
|
}
|
|
494
572
|
|
|
495
573
|
/**
|
|
@@ -515,6 +593,13 @@ function makeTypePick(type, context) {
|
|
|
515
593
|
if (type instanceof NumberType) {
|
|
516
594
|
return /** @type {any} */ (new NumberPick(type, context));
|
|
517
595
|
}
|
|
596
|
+
if (
|
|
597
|
+
type instanceof StringType ||
|
|
598
|
+
(type instanceof EnumerationType &&
|
|
599
|
+
type.values.every((value) => typeof value === 'string'))
|
|
600
|
+
) {
|
|
601
|
+
return /** @type {any} */ (new StringPick(type, context));
|
|
602
|
+
}
|
|
518
603
|
return /** @type {any} */ (new ValuePick(type, context));
|
|
519
604
|
}
|
|
520
605
|
|
|
@@ -532,10 +617,11 @@ function makePick(typeOrShape, context) {
|
|
|
532
617
|
/**
|
|
533
618
|
* @template {ObjectShape} T
|
|
534
619
|
* @param {ObjectType<T>} type
|
|
620
|
+
* @param {string} [tableName]
|
|
535
621
|
* @returns {RowPick<T>}
|
|
536
622
|
*/
|
|
537
|
-
function rowPick(type) {
|
|
538
|
-
return new RowPick(type);
|
|
623
|
+
function rowPick(type, tableName) {
|
|
624
|
+
return new RowPick(type, {tableName});
|
|
539
625
|
}
|
|
540
626
|
|
|
541
627
|
module.exports = {
|