xcraft-core-pickaxe 0.1.23 → 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;
@@ -23,7 +30,12 @@ const operators = {
23
30
 
24
31
  value({value}, context) {
25
32
  if (typeof value === 'boolean') {
26
- return value ? 'TRUE' : 'FALSE';
33
+ // return value ? 'TRUE' : 'FALSE';
34
+ //
35
+ // Value 1 and 0 works better with indexed content.
36
+ // For example, "json_extract(action, '$.path.to.value') IS FALSE"
37
+ // does not use the corresponding json_extract index.
38
+ return value ? 1 : 0;
27
39
  }
28
40
  if (!context.values) {
29
41
  return escape(value);
@@ -55,6 +67,8 @@ const operators = {
55
67
  // TODO: try values.push(path) => '?'
56
68
  // return `json_extract(${field}, ${sql(path, values)})`;
57
69
 
70
+ [value, path] = squashValueAndPath(value, path);
71
+
58
72
  if (value) {
59
73
  return `json_extract(${sql(value, context)}, ${escape(
60
74
  '$.' + path.join('.')
@@ -160,6 +174,17 @@ const operators = {
160
174
  return `(${sqlConditions.join(' OR ')})`;
161
175
  },
162
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
+
163
188
  abs({value}, context) {
164
189
  return `ABS(${sql(value, context)})`;
165
190
  },
@@ -270,14 +295,14 @@ const operators = {
270
295
  };
271
296
 
272
297
  /**
273
- * @param {Operator | AnyPick} operatorOrPick
274
- * @returns {Operator}
298
+ * @param {Expression | BasePick} expressionOrPick
299
+ * @returns {Expression}
275
300
  */
276
- function getOperator(operatorOrPick) {
277
- if (isAnyPick(operatorOrPick)) {
278
- return operatorOrPick.value;
301
+ function getExpression(expressionOrPick) {
302
+ if (isAnyPick(expressionOrPick)) {
303
+ return expressionOrPick.expression;
279
304
  }
280
- return operatorOrPick;
305
+ return expressionOrPick;
281
306
  }
282
307
 
283
308
  /**
@@ -286,24 +311,24 @@ function getOperator(operatorOrPick) {
286
311
  * scope?: any,
287
312
  * useTableNames?: boolean,
288
313
  * equalOperator: 'IS' | '=',
289
- * }} OperatorToSqlContext
314
+ * }} ExpressionToSqlContext
290
315
  */
291
316
 
292
317
  /**
293
- * @param {Operator | AnyPick} operatorOrPick
294
- * @param {OperatorToSqlContext} context
318
+ * @param {Expression | BasePick} expressionOrPick
319
+ * @param {ExpressionToSqlContext} context
295
320
  * @returns {string}
296
321
  */
297
- function sql(operatorOrPick, context) {
298
- const operator = getOperator(operatorOrPick);
299
- const operatorName = operator.operator;
322
+ function sql(expressionOrPick, context) {
323
+ const expression = getExpression(expressionOrPick);
324
+ const operatorName = expression.operator;
300
325
  if (!(operatorName in operators)) {
301
- throw new Error(`Unknown operator '${JSON.stringify(operator)}'`);
326
+ throw new Error(`Unknown operator for '${JSON.stringify(expression)}'`);
302
327
  }
303
- return operators[operatorName](operator, context);
328
+ return operators[operatorName](expression, context);
304
329
  }
305
330
 
306
- function operatorToSql(operator) {
331
+ function expressionToSql(operator) {
307
332
  const context = {
308
333
  values: [],
309
334
  };
@@ -315,5 +340,5 @@ function operatorToSql(operator) {
315
340
 
316
341
  module.exports = {
317
342
  sql,
318
- operatorToSql,
343
+ expressionToSql,
319
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
+ };