xcraft-core-pickaxe 0.1.24 → 0.1.26

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-inner-declarations */
2
2
  // @ts-check
3
- const {operatorToSql} = require('./operator-to-sql.js');
3
+ const {expressionToSql} = require('./operator-to-sql.js');
4
4
  const {
5
5
  string,
6
6
  number,
@@ -80,8 +80,8 @@ example1: {
80
80
  user.get('address').get('streetName').eq('Mine road')
81
81
  );
82
82
 
83
- console.log(operatorToSql(filter1));
84
- console.log(operatorToSql(filter2));
83
+ console.log(expressionToSql(filter1));
84
+ console.log(expressionToSql(filter2));
85
85
  }
86
86
 
87
87
  example2: {
@@ -1,9 +1,10 @@
1
1
  /* eslint-disable jsdoc/require-returns */
2
2
  // @ts-check
3
3
 
4
- const $ = require('xcraft-core-pickaxe/lib/operators.js');
4
+ const {pickOperators: $} = require('xcraft-core-pickaxe/lib/pick-operators.js');
5
5
  const {RecordPick, ValuePick} = require('./picks.js');
6
6
  const {StringType, NumberType} = require('xcraft-core-stones');
7
+ const {pickFromValue} = require('./pick-or-value.js');
7
8
 
8
9
  /**
9
10
  * @typedef {Record<string, number>} LevelSet
@@ -36,7 +37,7 @@ function levelSetHasValue2(set, key, value) {
36
37
  */
37
38
  function levelSetIsEmpty(set) {
38
39
  if (!(set instanceof RecordPick)) {
39
- return $.value(Object.keys(set).length === 0);
40
+ return pickFromValue(Object.keys(set).length === 0);
40
41
  }
41
42
  return set.keys().length.eq(0);
42
43
  }
@@ -1,12 +1,9 @@
1
1
  // @ts-check
2
2
 
3
- const {isAnyPick} = require('./picks.js');
3
+ const {isAnyPick, BasePick} = require('./picks.js');
4
4
 
5
5
  /**
6
- * @typedef {import("./operators.js").Operator} Operator
7
- */
8
- /**
9
- * @typedef {import('./picks.js').AnyPick} AnyPick
6
+ * @typedef {import("./operators.js").Expression} Expression
10
7
  */
11
8
 
12
9
  function escape(value) {
@@ -16,6 +13,16 @@ function escape(value) {
16
13
  return value;
17
14
  }
18
15
 
16
+ function squashValueAndPath(value, path) {
17
+ if (value) {
18
+ const operatorName = value.operator;
19
+ if (operatorName === 'get') {
20
+ return squashValueAndPath(value.value, [value.path, ...path]);
21
+ }
22
+ }
23
+ return [value, path];
24
+ }
25
+
19
26
  const operators = {
20
27
  unsafeSql({sql}, context) {
21
28
  return sql;
@@ -60,6 +67,8 @@ const operators = {
60
67
  // TODO: try values.push(path) => '?'
61
68
  // return `json_extract(${field}, ${sql(path, values)})`;
62
69
 
70
+ [value, path] = squashValueAndPath(value, path);
71
+
63
72
  if (value) {
64
73
  return `json_extract(${sql(value, context)}, ${escape(
65
74
  '$.' + path.join('.')
@@ -165,6 +174,17 @@ const operators = {
165
174
  return `(${sqlConditions.join(' OR ')})`;
166
175
  },
167
176
 
177
+ ifNull({a, b}, context) {
178
+ return `IFNULL(${sql(a, context)},${sql(b, context)})`;
179
+ },
180
+
181
+ if({condition, a, b}, context) {
182
+ return `IF(${sql(condition, context)},${sql(a, context)},${sql(
183
+ b,
184
+ context
185
+ )})`;
186
+ },
187
+
168
188
  abs({value}, context) {
169
189
  return `ABS(${sql(value, context)})`;
170
190
  },
@@ -275,14 +295,14 @@ const operators = {
275
295
  };
276
296
 
277
297
  /**
278
- * @param {Operator | AnyPick} operatorOrPick
279
- * @returns {Operator}
298
+ * @param {Expression | BasePick} expressionOrPick
299
+ * @returns {Expression}
280
300
  */
281
- function getOperator(operatorOrPick) {
282
- if (isAnyPick(operatorOrPick)) {
283
- return operatorOrPick.value;
301
+ function getExpression(expressionOrPick) {
302
+ if (isAnyPick(expressionOrPick)) {
303
+ return expressionOrPick.expression;
284
304
  }
285
- return operatorOrPick;
305
+ return expressionOrPick;
286
306
  }
287
307
 
288
308
  /**
@@ -291,24 +311,24 @@ function getOperator(operatorOrPick) {
291
311
  * scope?: any,
292
312
  * useTableNames?: boolean,
293
313
  * equalOperator: 'IS' | '=',
294
- * }} OperatorToSqlContext
314
+ * }} ExpressionToSqlContext
295
315
  */
296
316
 
297
317
  /**
298
- * @param {Operator | AnyPick} operatorOrPick
299
- * @param {OperatorToSqlContext} context
318
+ * @param {Expression | BasePick} expressionOrPick
319
+ * @param {ExpressionToSqlContext} context
300
320
  * @returns {string}
301
321
  */
302
- function sql(operatorOrPick, context) {
303
- const operator = getOperator(operatorOrPick);
304
- const operatorName = operator.operator;
322
+ function sql(expressionOrPick, context) {
323
+ const expression = getExpression(expressionOrPick);
324
+ const operatorName = expression.operator;
305
325
  if (!(operatorName in operators)) {
306
- throw new Error(`Unknown operator '${JSON.stringify(operator)}'`);
326
+ throw new Error(`Unknown operator for '${JSON.stringify(expression)}'`);
307
327
  }
308
- return operators[operatorName](operator, context);
328
+ return operators[operatorName](expression, context);
309
329
  }
310
330
 
311
- function operatorToSql(operator) {
331
+ function expressionToSql(operator) {
312
332
  const context = {
313
333
  values: [],
314
334
  };
@@ -320,5 +340,5 @@ function operatorToSql(operator) {
320
340
 
321
341
  module.exports = {
322
342
  sql,
323
- operatorToSql,
343
+ expressionToSql,
324
344
  };
package/lib/operators.js CHANGED
@@ -1,12 +1,7 @@
1
1
  /* eslint-disable jsdoc/require-returns */
2
2
  // @ts-check
3
3
 
4
- const {number, array} = require('xcraft-core-stones');
5
-
6
- /**
7
- * @template {AnyTypeOrShape} T
8
- * @typedef {import("./picks.js").ValuePick<T>} ValuePick
9
- */
4
+ const {number} = require('xcraft-core-stones');
10
5
 
11
6
  function op(value) {
12
7
  if (value === null) {
@@ -19,9 +14,9 @@ function op(value) {
19
14
  if (type === 'object' && 'operator' in value) {
20
15
  return value;
21
16
  }
22
- const {isAnyPick} = require('./picks.js');
17
+ const {isAnyPick} = /** @type {any} */ (require('./picks.js'));
23
18
  if (isAnyPick(value)) {
24
- return value.value;
19
+ return value.expression;
25
20
  }
26
21
 
27
22
  throw new Error(`Bad value '${value}'`);
@@ -222,6 +217,23 @@ const operators = {
222
217
  });
223
218
  },
224
219
 
220
+ ifNull(a, b) {
221
+ return /** @type {const} */ ({
222
+ operator: 'ifNull',
223
+ a,
224
+ b,
225
+ });
226
+ },
227
+
228
+ if(condition, a, b) {
229
+ return /** @type {const} */ ({
230
+ operator: 'if',
231
+ condition: op(condition),
232
+ a: op(a),
233
+ b: op(b),
234
+ });
235
+ },
236
+
225
237
  abs(value) {
226
238
  value = op(value);
227
239
  return /** @type {const} */ ({
@@ -330,69 +342,43 @@ const operators = {
330
342
  count(field, distinct) {
331
343
  return /** @type {const} */ ({
332
344
  operator: 'count',
333
- type: number,
334
345
  field,
335
346
  distinct,
336
347
  });
337
348
  },
338
349
 
339
- /**
340
- * @template {AnyTypeOrShape} T
341
- * @param {ValuePick<T>} field
342
- */
343
350
  avg(field) {
344
351
  return /** @type {const} */ ({
345
352
  operator: 'avg',
346
- type: field.type,
347
- field: field.value,
353
+ field,
348
354
  });
349
355
  },
350
356
 
351
- /**
352
- * @template {AnyTypeOrShape} T
353
- * @param {ValuePick<T>} field
354
- */
355
357
  max(field) {
356
358
  return /** @type {const} */ ({
357
359
  operator: 'max',
358
- type: field.type,
359
- field: field.value,
360
+ field,
360
361
  });
361
362
  },
362
363
 
363
- /**
364
- * @template {AnyTypeOrShape} T
365
- * @param {ValuePick<T>} field
366
- */
367
364
  min(field) {
368
365
  return /** @type {const} */ ({
369
366
  operator: 'min',
370
- type: field.type,
371
- field: field.value,
367
+ field,
372
368
  });
373
369
  },
374
370
 
375
- /**
376
- * @template {AnyTypeOrShape} T
377
- * @param {ValuePick<T>} field
378
- */
379
371
  sum(field) {
380
372
  return /** @type {const} */ ({
381
373
  operator: 'sum',
382
- type: field.type,
383
- field: field.value,
374
+ field,
384
375
  });
385
376
  },
386
377
 
387
- /**
388
- * @template {AnyTypeOrShape} T
389
- * @param {ValuePick<T>} field
390
- */
391
378
  groupArray(field) {
392
379
  return /** @type {const} */ ({
393
380
  operator: 'groupArray',
394
- type: array(field.type),
395
- field: field.value,
381
+ field,
396
382
  });
397
383
  },
398
384
  };
@@ -407,17 +393,7 @@ const operators = {
407
393
  */
408
394
 
409
395
  /**
410
- * @typedef {Values<{[k in keyof typeof operators] : ReturnType<typeof operators[k]>}>} Operator
411
- */
412
-
413
- /**
414
- * @typedef {Operators["count"] | Operators["avg"] | Operators["max"] | Operators["min"] | Operators["sum"] | Operators["groupArray"]} Aggregator
415
- */
416
- /**
417
- * @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"] | Operators["unsafeSql"]} BooleanOperator
418
- */
419
- /**
420
- * @typedef {Operators["abs"] | Operators["plus"] | Operators["minus"]} MathOperator
396
+ * @typedef {Values<{[k in keyof typeof operators] : ReturnType<typeof operators[k]>}>} Expression
421
397
  */
422
398
 
423
399
  module.exports = operators;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @template {AnyTypeOrShape} T
3
+ * @typedef {T extends OptionType ? T : OptionType<T>} WrapOption
4
+ */
5
+
6
+ const {toObjectType, OptionType, option} = require('xcraft-core-stones');
7
+
8
+ /**
9
+ * @template {ObjectShape} T
10
+ * @typedef {{[K in keyof T]: WrapOption<T[K]>}} OptionalObjectShape
11
+ */
12
+
13
+ /**
14
+ * @template {ObjectShape} T
15
+ * @param {ObjectType<T>} type
16
+ * @returns {ObjectType<OptionalObjectShape<T>>}
17
+ */
18
+ function optionalObjectType(type) {
19
+ return type.map((subType) =>
20
+ subType instanceof OptionType ? subType : option(subType)
21
+ );
22
+ }
23
+
24
+ /**
25
+ * @template {AnyObjectShape} T
26
+ * @param {T} shape
27
+ * @returns {OptionalObjectShape<GetShape<T>>}
28
+ */
29
+ function optionalObjectShape(shape) {
30
+ const type = toObjectType(shape);
31
+ const optionalType = optionalObjectType(type);
32
+ return optionalType.properties;
33
+ }
34
+
35
+ module.exports = {
36
+ optionalObjectType,
37
+ optionalObjectShape,
38
+ };
@@ -0,0 +1,128 @@
1
+ /* eslint-disable jsdoc/require-returns */
2
+ // @ts-check
3
+
4
+ const {
5
+ array,
6
+ number,
7
+ boolean,
8
+ union,
9
+ UnionType,
10
+ any,
11
+ } = require('xcraft-core-stones');
12
+ const $o = require('./operators.js');
13
+ const {
14
+ BasePick,
15
+ ValuePick,
16
+ BooleanPick,
17
+ NumberPick,
18
+ makePick,
19
+ ArrayPick,
20
+ } = require('./picks.js');
21
+ const {getPickOrValueType} = require('./pick-or-value.js');
22
+
23
+ /**
24
+ * @typedef {import('./pick-or-value.js').PickOrValue} PickOrValue
25
+ */
26
+
27
+ /**
28
+ * @template {PickOrValue} T
29
+ * @typedef {import('./pick-or-value.js').PickOrValueType<T>} PickOrValueType
30
+ */
31
+
32
+ const pickOperators = {
33
+ ...$o,
34
+
35
+ /**
36
+ * @param {string} sql
37
+ */
38
+ unsafeSql(sql) {
39
+ return new BasePick(any, $o.unsafeSql(sql));
40
+ },
41
+
42
+ /**
43
+ * @param {BooleanPick} value
44
+ * @returns {BooleanPick}
45
+ */
46
+ not(value) {
47
+ return new BooleanPick(boolean, $o.not(value));
48
+ },
49
+
50
+ /**
51
+ * @param {BooleanPick[]} conditions
52
+ * @returns {BooleanPick}
53
+ */
54
+ and(...conditions) {
55
+ return new BooleanPick(boolean, $o.and(...conditions));
56
+ },
57
+
58
+ /**
59
+ * @param {BooleanPick[]} conditions
60
+ * @returns {BooleanPick}
61
+ */
62
+ or(...conditions) {
63
+ return new BooleanPick(boolean, $o.or(...conditions));
64
+ },
65
+
66
+ /**
67
+ * @template {PickOrValue} T
68
+ * @template {PickOrValue} U
69
+ * @param {BooleanPick} condition
70
+ * @param {T} a
71
+ * @param {U} b
72
+ * @returns {ValuePick<UnionType<[PickOrValueType<T>, PickOrValueType<U>]>>}
73
+ */
74
+ if(condition, a, b) {
75
+ const newType = union(getPickOrValueType(a), getPickOrValueType(b));
76
+ return new ValuePick(newType, $o.if(condition, a, b));
77
+ },
78
+
79
+ /**
80
+ * @param {BasePick} [field]
81
+ * @param {boolean} [distinct]
82
+ */
83
+ count(field, distinct) {
84
+ return new NumberPick(number, $o.count(field, distinct));
85
+ },
86
+
87
+ /**
88
+ * @param {NumberPick} field
89
+ */
90
+ avg(field) {
91
+ return new NumberPick(number, $o.avg(field));
92
+ },
93
+
94
+ /**
95
+ * @template {AnyTypeOrShape} T
96
+ * @param {ValuePick<T>} field
97
+ */
98
+ max(field) {
99
+ return makePick(field.type, $o.max(field));
100
+ },
101
+
102
+ /**
103
+ * @template {AnyTypeOrShape} T
104
+ * @param {ValuePick<T>} field
105
+ */
106
+ min(field) {
107
+ return makePick(field.type, $o.min(field));
108
+ },
109
+
110
+ /**
111
+ * @param {NumberPick} field
112
+ */
113
+ sum(field) {
114
+ return new NumberPick(number, $o.sum(field));
115
+ },
116
+
117
+ /**
118
+ * @template {AnyTypeOrShape} T
119
+ * @param {ValuePick<T>} field
120
+ */
121
+ groupArray(field) {
122
+ return new ArrayPick(array(field.type), $o.groupArray(field));
123
+ },
124
+ };
125
+
126
+ module.exports = {
127
+ pickOperators,
128
+ };
@@ -0,0 +1,76 @@
1
+ /* eslint-disable jsdoc/require-returns */
2
+ // @ts-check
3
+
4
+ const {
5
+ number,
6
+ NumberType,
7
+ string,
8
+ StringType,
9
+ boolean,
10
+ BooleanType,
11
+ } = require('xcraft-core-stones');
12
+ const {BasePick, makeTypePick} = require('./picks.js');
13
+ const $o = require('./operators.js');
14
+
15
+ /**
16
+ * @typedef {string | number | boolean} PickValue
17
+ */
18
+
19
+ /**
20
+ * @typedef {BasePick<any> | PickValue} PickOrValue
21
+ */
22
+
23
+ /**
24
+ * @template {PickValue} T
25
+ * @typedef {T extends boolean ? BooleanType : T extends number ? NumberType : T extends string ? StringType : never} GetValueType
26
+ */
27
+
28
+ /**
29
+ * @template {PickOrValue} T
30
+ * @typedef {T extends BasePick<infer U> ? U : T extends PickValue ? GetValueType<T> : never} PickOrValueType
31
+ */
32
+
33
+ /**
34
+ * @template {PickValue} T
35
+ * @param {T} value
36
+ * @returns {GetValueType<T>}
37
+ */
38
+ function getValueType(value) {
39
+ if (typeof value === 'string') {
40
+ return /** @type {any} */ (string);
41
+ }
42
+ if (typeof value === 'number') {
43
+ return /** @type {any} */ (number);
44
+ }
45
+ if (typeof value === 'boolean') {
46
+ return /** @type {any} */ (boolean);
47
+ }
48
+ throw new Error('Unknown value type');
49
+ }
50
+
51
+ /**
52
+ * @template {PickOrValue} T
53
+ * @param {T} pickOrValue
54
+ * @returns {PickOrValueType<T>}
55
+ */
56
+ function getPickOrValueType(pickOrValue) {
57
+ if (pickOrValue instanceof BasePick) {
58
+ return pickOrValue.type;
59
+ }
60
+ return /** @type {any} */ (getValueType(pickOrValue));
61
+ }
62
+
63
+ /**
64
+ * @template {PickValue} T
65
+ * @param {T} value
66
+ */
67
+ function pickFromValue(value) {
68
+ const type = getValueType(value);
69
+ return makeTypePick(type, $o.value(value));
70
+ }
71
+
72
+ module.exports = {
73
+ getPickOrValueType,
74
+ getValueType,
75
+ pickFromValue,
76
+ };