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.
@@ -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
  /**
@@ -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, false).sql;
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
  /**
@@ -45,14 +45,16 @@ function groupByFields(groupBy, values) {
45
45
 
46
46
  /**
47
47
  * @param {QueryObj} query
48
- * @param {boolean} [useBindedValues=true]
48
+ * @param {any[] | null} [values]
49
49
  * @returns {{sql: string, values: any[] | null}}
50
50
  */
51
- function queryToSql(query, useBindedValues = true) {
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 ${query.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.6",
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.1.0"
25
+ "xcraft-dev-rules": "^4.3.0"
26
26
  },
27
27
  "prettier": "xcraft-dev-prettier"
28
28
  }