@uwdata/mosaic-sql 0.13.0 → 0.14.1
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/dist/types/ast/aggregate.d.ts +5 -4
- package/dist/types/ast/case.d.ts +6 -7
- package/dist/types/ast/column-param.d.ts +7 -7
- package/dist/types/ast/column-ref.d.ts +7 -6
- package/dist/types/ast/param.d.ts +5 -4
- package/dist/types/ast/query.d.ts +46 -39
- package/dist/types/ast/window.d.ts +15 -12
- package/dist/types/functions/aggregate.d.ts +89 -88
- package/dist/types/functions/case.d.ts +6 -8
- package/dist/types/functions/column.d.ts +5 -3
- package/dist/types/functions/datetime.d.ts +12 -11
- package/dist/types/functions/numeric.d.ts +48 -46
- package/dist/types/functions/operators.d.ts +80 -78
- package/dist/types/functions/order-by.d.ts +5 -4
- package/dist/types/functions/spatial.d.ts +14 -13
- package/dist/types/functions/sql-template-tag.d.ts +4 -5
- package/dist/types/functions/string.d.ts +22 -20
- package/dist/types/functions/util.d.ts +8 -0
- package/dist/types/functions/window.d.ts +18 -16
- package/dist/types/index.d.ts +5 -0
- package/dist/types/transforms/bin-1d.d.ts +3 -2
- package/dist/types/transforms/bin-2d.d.ts +6 -5
- package/dist/types/transforms/bin-date.d.ts +44 -0
- package/dist/types/transforms/bin-histogram.d.ts +51 -0
- package/dist/types/transforms/bin-linear-1d.d.ts +6 -4
- package/dist/types/transforms/bin-linear-2d.d.ts +6 -5
- package/dist/types/transforms/line-density.d.ts +5 -4
- package/dist/types/transforms/m4.d.ts +7 -4
- package/dist/types/transforms/util/bin-step.d.ts +61 -0
- package/dist/types/transforms/util/time-interval.d.ts +13 -0
- package/dist/types/types.d.ts +1 -0
- package/dist/types/util/ast.d.ts +6 -5
- package/dist/types/util/function.d.ts +6 -4
- package/dist/types/util/type-check.d.ts +6 -2
- package/dist/types/visit/visitors.d.ts +3 -2
- package/dist/types/visit/walk.d.ts +7 -4
- package/package.json +2 -2
- package/src/ast/aggregate.js +5 -2
- package/src/ast/case.js +6 -5
- package/src/ast/column-param.js +7 -5
- package/src/ast/column-ref.js +6 -3
- package/src/ast/param.js +5 -2
- package/src/ast/query.js +23 -19
- package/src/ast/window.js +10 -6
- package/src/functions/aggregate.js +55 -52
- package/src/functions/case.js +7 -7
- package/src/functions/column.js +6 -2
- package/src/functions/datetime.js +9 -6
- package/src/functions/numeric.js +35 -31
- package/src/functions/operators.js +53 -50
- package/src/functions/order-by.js +5 -2
- package/src/functions/spatial.js +10 -7
- package/src/functions/sql-template-tag.js +5 -5
- package/src/functions/string.js +16 -13
- package/src/functions/util.js +14 -0
- package/src/functions/window.js +13 -10
- package/src/index.js +6 -0
- package/src/transforms/bin-1d.js +4 -1
- package/src/transforms/bin-2d.js +7 -4
- package/src/transforms/bin-date.js +37 -0
- package/src/transforms/bin-histogram.js +52 -0
- package/src/transforms/bin-linear-1d.js +7 -3
- package/src/transforms/bin-linear-2d.js +12 -8
- package/src/transforms/line-density.js +7 -3
- package/src/transforms/m4.js +7 -3
- package/src/transforms/util/bin-step.js +79 -0
- package/src/transforms/util/time-interval.js +97 -0
- package/src/types.ts +11 -0
- package/src/util/ast.js +6 -3
- package/src/util/function.js +6 -2
- package/src/util/type-check.js +5 -1
- package/src/visit/visitors.js +6 -2
- package/src/visit/walk.js +8 -3
package/src/functions/window.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @import { WindowNode } from '../ast/window.js'
|
|
3
|
+
* @import { ExprValue, NumberValue } from '../types.js'
|
|
4
|
+
*/
|
|
2
5
|
import { winFn } from '../util/function.js';
|
|
3
6
|
|
|
4
7
|
/**
|
|
@@ -49,7 +52,7 @@ export function cume_dist() {
|
|
|
49
52
|
/**
|
|
50
53
|
* Create a window function that returns an integer ranging from 1 to the
|
|
51
54
|
* argument value, dividing the partition as equally as possible.
|
|
52
|
-
* @param {
|
|
55
|
+
* @param {NumberValue} num_buckets The number of
|
|
53
56
|
* quantile buckets.
|
|
54
57
|
* @returns {WindowNode}
|
|
55
58
|
*/
|
|
@@ -63,8 +66,8 @@ export function ntile(num_buckets) {
|
|
|
63
66
|
* If there is no such row, instead return default (which must be of the same
|
|
64
67
|
* type as the expression). Both offset and default are evaluated with respect
|
|
65
68
|
* to the current row. If omitted, offset defaults to 1 and default to null.
|
|
66
|
-
* @param {
|
|
67
|
-
* @param {
|
|
69
|
+
* @param {ExprValue} expr The expression to evaluate.
|
|
70
|
+
* @param {NumberValue} [offset] The row offset
|
|
68
71
|
* (default `1`).
|
|
69
72
|
* @param {*} [defaultValue] The default value (default `null`).
|
|
70
73
|
* @returns {WindowNode}
|
|
@@ -79,8 +82,8 @@ export function lag(expr, offset, defaultValue){
|
|
|
79
82
|
* If there is no such row, instead return default (which must be of the same
|
|
80
83
|
* type as the expression). Both offset and default are evaluated with respect
|
|
81
84
|
* to the current row. If omitted, offset defaults to 1 and default to null.
|
|
82
|
-
* @param {
|
|
83
|
-
* @param {
|
|
85
|
+
* @param {ExprValue} expr The expression to evaluate.
|
|
86
|
+
* @param {NumberValue} [offset] The row offset
|
|
84
87
|
* (default `1`).
|
|
85
88
|
* @param {*} [defaultValue] The default value (default `null`).
|
|
86
89
|
* @returns {WindowNode}
|
|
@@ -92,7 +95,7 @@ export function lead(expr, offset, defaultValue){
|
|
|
92
95
|
/**
|
|
93
96
|
* Create a window function that returns the expression evaluated at the row
|
|
94
97
|
* that is the first row of the window frame.
|
|
95
|
-
* @param {
|
|
98
|
+
* @param {ExprValue} expr The expression to evaluate.
|
|
96
99
|
* @returns {WindowNode}
|
|
97
100
|
*/
|
|
98
101
|
export function first_value(expr) {
|
|
@@ -102,7 +105,7 @@ export function first_value(expr) {
|
|
|
102
105
|
/**
|
|
103
106
|
* Create a window function that returns the expression evaluated at the row
|
|
104
107
|
* that is the last row of the window frame.
|
|
105
|
-
* @param {
|
|
108
|
+
* @param {ExprValue} expr The expression to evaluate.
|
|
106
109
|
* @returns {WindowNode}
|
|
107
110
|
*/
|
|
108
111
|
export function last_value(expr) {
|
|
@@ -112,8 +115,8 @@ export function last_value(expr) {
|
|
|
112
115
|
/**
|
|
113
116
|
* Create a window function that returns the expression evaluated at the
|
|
114
117
|
* nth row of the window frame (counting from 1), or null if no such row.
|
|
115
|
-
* @param {
|
|
116
|
-
* @param {
|
|
118
|
+
* @param {ExprValue} expr The expression to evaluate.
|
|
119
|
+
* @param {NumberValue} nth The 1-based window frame index.
|
|
117
120
|
* @returns {WindowNode}
|
|
118
121
|
*/
|
|
119
122
|
export function nth_value(expr, nth) {
|
package/src/index.js
CHANGED
|
@@ -37,6 +37,7 @@ export { asc, desc } from './functions/order-by.js';
|
|
|
37
37
|
export { geojson, x, y, centroid, centroidX, centroidY } from './functions/spatial.js';
|
|
38
38
|
export { sql } from './functions/sql-template-tag.js';
|
|
39
39
|
export { regexp_matches, contains, prefix, suffix, lower, upper, length } from './functions/string.js';
|
|
40
|
+
export { coalesce } from './functions/util.js';
|
|
40
41
|
export { cume_dist, dense_rank, first_value, lag, last_value, lead, nth_value, ntile, percent_rank, rank, row_number } from './functions/window.js';
|
|
41
42
|
|
|
42
43
|
export { rewrite } from './visit/rewrite.js';
|
|
@@ -49,6 +50,8 @@ export { loadCSV, loadJSON, loadObjects, loadParquet, loadSpatial } from './load
|
|
|
49
50
|
|
|
50
51
|
export { bin1d } from './transforms/bin-1d.js';
|
|
51
52
|
export { bin2d } from './transforms/bin-2d.js';
|
|
53
|
+
export { binDate } from './transforms/bin-date.js';
|
|
54
|
+
export { binHistogram } from './transforms/bin-histogram.js';
|
|
52
55
|
export { binLinear1d } from './transforms/bin-linear-1d.js';
|
|
53
56
|
export { binLinear2d } from './transforms/bin-linear-2d.js';
|
|
54
57
|
export { lineDensity } from './transforms/line-density.js';
|
|
@@ -57,3 +60,6 @@ export { scaleTransform } from './transforms/scales.js';
|
|
|
57
60
|
|
|
58
61
|
export { asLiteral, asNode, asTableRef, asVerbatim, over } from './util/ast.js';
|
|
59
62
|
export { isParamLike } from './util/type-check.js';
|
|
63
|
+
|
|
64
|
+
export { binSpec, binStep } from './transforms/util/bin-step.js';
|
|
65
|
+
export { timeInterval } from './transforms/util/time-interval.js';
|
package/src/transforms/bin-1d.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ExprValue } from '../types.js'
|
|
3
|
+
*/
|
|
1
4
|
import { float64 } from '../functions/cast.js';
|
|
2
5
|
import { mul, sub } from '../functions/operators.js';
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Compute binned values over a one-dimensional extent.
|
|
6
|
-
* @param {
|
|
9
|
+
* @param {ExprValue} x The expression to bin.
|
|
7
10
|
* The expression must return numeric values. For example, to bin
|
|
8
11
|
* datetime values, the input expression might map them to numeric
|
|
9
12
|
* values such as milliseconds since the epoch.
|
package/src/transforms/bin-2d.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @import { SelectQuery } from '../ast/query.js'
|
|
3
|
+
* @import { ExprValue } from '../types.js'
|
|
4
|
+
*/
|
|
2
5
|
import { int32 } from '../functions/cast.js';
|
|
3
6
|
import { floor } from '../functions/numeric.js';
|
|
4
7
|
import { add, mul } from '../functions/operators.js';
|
|
@@ -11,9 +14,9 @@ import { add, mul } from '../functions/operators.js';
|
|
|
11
14
|
* and uses a 2D integer bin index of the form (xbin + num_xbins * ybin).
|
|
12
15
|
* @param {SelectQuery} q The input query. The FROM and WHERE clauses should
|
|
13
16
|
* be added to the query separately, either before or after this method.
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
16
|
-
* @param {Record<string,
|
|
17
|
+
* @param {ExprValue} xp The x bin expression.
|
|
18
|
+
* @param {ExprValue} yp The y bin expression.
|
|
19
|
+
* @param {Record<string, ExprValue>} aggs Named
|
|
17
20
|
* aggregate expressions over bins.
|
|
18
21
|
* @param {number} xn The number of bins along the x dimension
|
|
19
22
|
* @param {string[]} groupby Group by expressions.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ExprNode } from '../ast/node.js'
|
|
3
|
+
* @import { ExprValue, TimeUnit } from '../types.js'
|
|
4
|
+
*/
|
|
5
|
+
import { dateBin, interval } from '../functions/datetime.js';
|
|
6
|
+
import { add } from '../functions/operators.js';
|
|
7
|
+
import { timeInterval } from './util/time-interval.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {object} BinDateOptions
|
|
11
|
+
* @property {TimeUnit} [interval] A string indicating a time interval
|
|
12
|
+
* unit, such as 'year', 'day', or 'hour'.
|
|
13
|
+
* @property {number} [step] The number of time interval steps to
|
|
14
|
+
* take, such as 2 years or 3 months.
|
|
15
|
+
* @property {number} [offset] The number of bin steps (default 0) by
|
|
16
|
+
* which to offset the result.
|
|
17
|
+
* @property {number} [steps] The desired number of binning steps.
|
|
18
|
+
* This value is a hint, it does not guarantee an exact number of steps.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Return a SQL expression for date/time bins.
|
|
23
|
+
* @param {ExprValue} field The column or expression to bin.
|
|
24
|
+
* @param {[Date|number, Date|number]} extent The min/max extent over which to bin.
|
|
25
|
+
* @param {BinDateOptions} [options] Datetime binning options.
|
|
26
|
+
* @returns {ExprNode}
|
|
27
|
+
*/
|
|
28
|
+
export function binDate(field, extent, options = {}) {
|
|
29
|
+
const { offset = 0 } = options;
|
|
30
|
+
|
|
31
|
+
// use interval if provided, otherwise determine from extent
|
|
32
|
+
const { unit, step = 1 } = options.interval
|
|
33
|
+
? { unit: options.interval, step: options.step }
|
|
34
|
+
: timeInterval(extent[0], extent[1], options.steps || 40);
|
|
35
|
+
const bin = dateBin(field, unit, step);
|
|
36
|
+
return offset ? add(bin, interval(unit, offset * step)) : bin;
|
|
37
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ExprNode } from '../ast/node.js'
|
|
3
|
+
* @import { ExprValue } from '../types.js'
|
|
4
|
+
*/
|
|
5
|
+
import { float64 } from '../functions/cast.js';
|
|
6
|
+
import { floor } from '../functions/numeric.js';
|
|
7
|
+
import { add, div, mul, sub } from '../functions/operators.js';
|
|
8
|
+
import { binSpec } from './util/bin-step.js';
|
|
9
|
+
import { scaleTransform } from './scales.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {object} BinHistogramOptions
|
|
13
|
+
* @property {number} [step] An exact binning step to use.
|
|
14
|
+
* @property {number} [steps] The desired number of binning steps.
|
|
15
|
+
* This value is a hint, it does not guarantee an exact number of steps.
|
|
16
|
+
* @property {number} [minstep] A minimum binning step value. No generated
|
|
17
|
+
* step can be less than this value.
|
|
18
|
+
* @property {boolean} [nice] A boolean flag (default true) indicating if bin
|
|
19
|
+
* extents should be snapped to "nice" numbers such as multiples of 5 or 10.
|
|
20
|
+
* @property {number} [offset] The number of bin steps (default 0) by
|
|
21
|
+
* which to offset the result.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Return a SQL expression for histogram bins.
|
|
26
|
+
* @param {ExprValue} field The column or expression to bin.
|
|
27
|
+
* @param {[number, number]} extent The min/max extent over which to bin.
|
|
28
|
+
* @param {BinHistogramOptions} [options] Binning options.
|
|
29
|
+
* @param {ReturnType<typeof scaleTransform>} [transform] Scale transforms to
|
|
30
|
+
* apply to create (potentially non-linear) binning intervals.
|
|
31
|
+
* @returns {ExprNode} The resulting SQL expression
|
|
32
|
+
*/
|
|
33
|
+
export function binHistogram(
|
|
34
|
+
field,
|
|
35
|
+
extent,
|
|
36
|
+
options = {},
|
|
37
|
+
transform = scaleTransform({ type: 'linear' })
|
|
38
|
+
) {
|
|
39
|
+
const [min, max] = extent;
|
|
40
|
+
const { offset = 0 } = options;
|
|
41
|
+
const { apply, sqlApply, sqlInvert } = transform;
|
|
42
|
+
const b = binSpec(apply(min), apply(max), options);
|
|
43
|
+
const col = sqlApply(field);
|
|
44
|
+
const alpha = (b.max - b.min) / b.steps;
|
|
45
|
+
|
|
46
|
+
let expr = b.min === 0 ? col : sub(col, b.min);
|
|
47
|
+
if (alpha !== 1) expr = div(expr, float64(alpha));
|
|
48
|
+
expr = floor(offset ? add(offset, expr) : expr);
|
|
49
|
+
if (alpha !== 1) expr = mul(alpha, expr);
|
|
50
|
+
if (b.min !== 0) expr = add(b.min, expr);
|
|
51
|
+
return sqlInvert(expr);
|
|
52
|
+
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { SelectQuery } from '../ast/query.js'
|
|
3
|
+
* @import { ExprValue } from '../types.js'
|
|
4
|
+
*/
|
|
1
5
|
import { Query } from '../ast/query.js';
|
|
2
6
|
import { sum } from '../functions/aggregate.js';
|
|
3
7
|
import { int32 } from '../functions/cast.js';
|
|
@@ -6,9 +10,9 @@ import { add, mul, neq, sub } from '../functions/operators.js';
|
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* Perform linear binning in one dimension.
|
|
9
|
-
* @param {
|
|
10
|
-
* @param {
|
|
11
|
-
* @param {
|
|
13
|
+
* @param {SelectQuery} query The base query to bin.
|
|
14
|
+
* @param {ExprValue} x The expression to bin.
|
|
15
|
+
* @param {ExprValue} [weight] The expression to weight by.
|
|
12
16
|
* @param {string[]} [groupby] Group by expressions.
|
|
13
17
|
* @returns {Query}
|
|
14
18
|
*/
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @import { SelectQuery } from '../ast/query.js'
|
|
3
|
+
* @import { ExprValue } from '../types.js'
|
|
4
|
+
*/
|
|
5
|
+
import { Query } from '../ast/query.js';
|
|
2
6
|
import { sum } from '../functions/aggregate.js';
|
|
3
7
|
import { int32 } from '../functions/cast.js';
|
|
4
8
|
import { floor } from '../functions/numeric.js';
|
|
@@ -22,9 +26,9 @@ function identity(x) {
|
|
|
22
26
|
* should be in units of grid indices, but can contain fractional components.
|
|
23
27
|
* @param {SelectQuery} q The input query. The FROM and WHERE clauses should
|
|
24
28
|
* be added to the query separately, before this method is invoked.
|
|
25
|
-
* @param {
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {
|
|
29
|
+
* @param {ExprValue} xp The x grid bin expression
|
|
30
|
+
* @param {ExprValue} yp The y grid bin expression
|
|
31
|
+
* @param {ExprValue | undefined} weight Point weights.
|
|
28
32
|
* @param {number} xn The number of x grid bins.
|
|
29
33
|
* @param {string[]} [groupby] Group by expressions.
|
|
30
34
|
* @returns {SelectQuery} A linear binning query for bin `index` and
|
|
@@ -35,13 +39,13 @@ export function binLinear2d(q, xp, yp, weight, xn, groupby = []) {
|
|
|
35
39
|
const w = weight ? x => mul(x, weight) : identity;
|
|
36
40
|
|
|
37
41
|
/**
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {
|
|
42
|
+
* @param {ExprValue} i
|
|
43
|
+
* @param {ExprValue} w
|
|
40
44
|
*/
|
|
41
45
|
const subq = (i, w) => q.clone().select({ xp, yp, i, w });
|
|
42
46
|
/**
|
|
43
|
-
* @param {
|
|
44
|
-
* @param {
|
|
47
|
+
* @param {ExprValue} x
|
|
48
|
+
* @param {ExprValue} y
|
|
45
49
|
*/
|
|
46
50
|
const index = (x, y) => add(x, mul(y, xn));
|
|
47
51
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @import { SelectQuery } from '../ast/query.js'
|
|
3
|
+
* @import { ExprValue } from '../types.js'
|
|
4
|
+
*/
|
|
5
|
+
import { Query } from '../ast/query.js';
|
|
2
6
|
import { count, max, sum } from '../functions/aggregate.js';
|
|
3
7
|
import { int32 } from '../functions/cast.js';
|
|
4
8
|
import { abs, floor, greatest, round, sign } from '../functions/numeric.js';
|
|
@@ -15,9 +19,9 @@ import { over } from '../util/ast.js';
|
|
|
15
19
|
* and then sum results for all line series to produce a density map.
|
|
16
20
|
* Based on Moritz and Fisher's work: https://arxiv.org/abs/1808.06019
|
|
17
21
|
* @param {SelectQuery} q The base query over the data.
|
|
18
|
-
* @param {
|
|
22
|
+
* @param {ExprValue} x Bin expression for x dimension.
|
|
19
23
|
* Provides gridded x coordinates, potentially with a fractional component.
|
|
20
|
-
* @param {
|
|
24
|
+
* @param {ExprValue} y Bin expression for x dimension.
|
|
21
25
|
* Provides gridded y coordinates, potentially with a fractional component.
|
|
22
26
|
* @param {string[]} z Group by columns that segment data into individual line
|
|
23
27
|
* series. An empty array indicates there is only a single line series.
|
package/src/transforms/m4.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ExprNode } from '../ast/node.js'
|
|
3
|
+
* @import { ExprValue, FromExpr } from '../types.js'
|
|
4
|
+
*/
|
|
1
5
|
import { Query, isQuery } from '../ast/query.js';
|
|
2
6
|
import { argmax, argmin, max, min } from '../functions/aggregate.js';
|
|
3
7
|
import { int32 } from '../functions/cast.js';
|
|
@@ -11,12 +15,12 @@ import { floor } from '../functions/numeric.js';
|
|
|
11
15
|
* argmin and argmax, following https://arxiv.org/pdf/2306.03714.pdf.
|
|
12
16
|
* This method can bin along either the *x* or *y* dimension, as determined
|
|
13
17
|
* by the caller-provided *bin* expression.
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
18
|
+
* @param {FromExpr} input The base query or table.
|
|
19
|
+
* @param {ExprValue} bin An expression that maps
|
|
16
20
|
* time-series values to fractional pixel positions.
|
|
17
21
|
* @param {string} x The x dimension column name.
|
|
18
22
|
* @param {string} y The y dimension column name.
|
|
19
|
-
* @param {
|
|
23
|
+
* @param {ExprNode[]} [groups] Additional
|
|
20
24
|
* groupby columns, for example for faceted charts.
|
|
21
25
|
* @returns {Query} The resulting M4 query.
|
|
22
26
|
*/
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} BinOptions
|
|
3
|
+
* @property {number} [step] An exact binning step to use.
|
|
4
|
+
* @property {number} [steps] The desired number of binning steps.
|
|
5
|
+
* This value is a hint, it does not guarantee an exact number of steps.
|
|
6
|
+
* @property {number} [minstep] A minimum binning step value. No generated
|
|
7
|
+
* step can be less than this value.
|
|
8
|
+
* @property {boolean} [nice] A boolean flag (default true) indicating if bin
|
|
9
|
+
* extents should be snapped to "nice" numbers such as multiples of 5 or 10.
|
|
10
|
+
* @property {number} [base] A number indicating the the logarithm base to
|
|
11
|
+
* use for automatic step size determination. Defaults to base 10.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate a numeric binning scheme suitable for a histogram.
|
|
16
|
+
* @param {number} min The minimum value of the extent to bin.
|
|
17
|
+
* @param {number} max The maximum value of the extent to bin.
|
|
18
|
+
* @param {BinOptions} options Binning scheme options.
|
|
19
|
+
*/
|
|
20
|
+
export function binSpec(min, max, options) {
|
|
21
|
+
let {
|
|
22
|
+
step,
|
|
23
|
+
steps = 25,
|
|
24
|
+
minstep = 0,
|
|
25
|
+
nice = true,
|
|
26
|
+
base
|
|
27
|
+
} = options;
|
|
28
|
+
|
|
29
|
+
if (nice !== false) {
|
|
30
|
+
// use span to determine step size
|
|
31
|
+
const span = max - min;
|
|
32
|
+
const logb = base ? Math.log(base) : Math.LN10;
|
|
33
|
+
step = step || binStep(span, steps, minstep, logb);
|
|
34
|
+
|
|
35
|
+
// adjust min/max relative to step
|
|
36
|
+
let v = Math.log(step);
|
|
37
|
+
const precision = v >= 0 ? 0 : ~~(-v / logb) + 1;
|
|
38
|
+
const eps = Math.pow(10, -precision - 1);
|
|
39
|
+
v = Math.floor(min / step + eps) * step;
|
|
40
|
+
min = min < v ? v - step : v;
|
|
41
|
+
max = Math.ceil(max / step) * step;
|
|
42
|
+
steps = Math.round((max - min) / step);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { min, max, steps };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Determine a bin step interval.
|
|
50
|
+
* @param {number} span The span from maximum to minimum value.
|
|
51
|
+
* @param {number} steps The approximate number of desired bins.
|
|
52
|
+
* @param {number} [minstep=0] The minimum acceptable bin step size.
|
|
53
|
+
* @param {number} [logb=Math.LN10] The log base for determining
|
|
54
|
+
* orders of magnitude for step sizes. Defaults to log base 10
|
|
55
|
+
* (`Math.LN10`). For example to use log base 2, provide the
|
|
56
|
+
* argument `Math.LN2` instead.
|
|
57
|
+
* @returns {number} The bin step interval (bin size).
|
|
58
|
+
*/
|
|
59
|
+
export function binStep(span, steps, minstep = 0, logb = Math.LN10) {
|
|
60
|
+
let v;
|
|
61
|
+
|
|
62
|
+
const level = Math.ceil(Math.log(steps) / logb);
|
|
63
|
+
let step = Math.max(
|
|
64
|
+
minstep,
|
|
65
|
+
Math.pow(10, Math.round(Math.log(span) / logb) - level)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// increase step size if too many bins
|
|
69
|
+
while (Math.ceil(span / step) > steps) { step *= 10; }
|
|
70
|
+
|
|
71
|
+
// decrease step size if allowed
|
|
72
|
+
const div = [5, 2];
|
|
73
|
+
for (let i = 0, n = div.length; i < n; ++i) {
|
|
74
|
+
v = step / div[i];
|
|
75
|
+
if (v >= minstep && span / v <= steps) step = v;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return step;
|
|
79
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** @import { TimeUnit } from '../../types.js' */
|
|
2
|
+
import { binStep } from './bin-step.js';
|
|
3
|
+
|
|
4
|
+
const YEAR = 'year';
|
|
5
|
+
const MONTH = 'month';
|
|
6
|
+
const DAY = 'day';
|
|
7
|
+
const HOUR = 'hour';
|
|
8
|
+
const MINUTE = 'minute';
|
|
9
|
+
const SECOND = 'second';
|
|
10
|
+
const MILLISECOND = 'millisecond';
|
|
11
|
+
const MICROSECOND = 'microsecond';
|
|
12
|
+
|
|
13
|
+
const durationSecond = 1000;
|
|
14
|
+
const durationMinute = durationSecond * 60;
|
|
15
|
+
const durationHour = durationMinute * 60;
|
|
16
|
+
const durationDay = durationHour * 24;
|
|
17
|
+
const durationWeek = durationDay * 7;
|
|
18
|
+
const durationMonth = durationDay * 30;
|
|
19
|
+
const durationYear = durationDay * 365;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @type {Array<{unit: TimeUnit, step: number, dt: number}>}
|
|
23
|
+
*/
|
|
24
|
+
const units = [
|
|
25
|
+
{ unit: SECOND, step: 1, dt: durationSecond },
|
|
26
|
+
{ unit: SECOND, step: 5, dt: durationSecond * 5 },
|
|
27
|
+
{ unit: SECOND, step: 15, dt: durationSecond * 15 },
|
|
28
|
+
{ unit: SECOND, step: 30, dt: durationSecond * 30 },
|
|
29
|
+
{ unit: MINUTE, step: 1, dt: durationMinute },
|
|
30
|
+
{ unit: MINUTE, step: 5, dt: durationMinute * 5 },
|
|
31
|
+
{ unit: MINUTE, step: 15, dt: durationMinute * 15 },
|
|
32
|
+
{ unit: MINUTE, step: 30, dt: durationMinute * 30 },
|
|
33
|
+
{ unit: HOUR, step: 1, dt: durationHour },
|
|
34
|
+
{ unit: HOUR, step: 3, dt: durationHour * 3 },
|
|
35
|
+
{ unit: HOUR, step: 6, dt: durationHour * 6 },
|
|
36
|
+
{ unit: HOUR, step: 12, dt: durationHour * 12 },
|
|
37
|
+
{ unit: DAY, step: 1, dt: durationDay },
|
|
38
|
+
{ unit: DAY, step: 7, dt: durationWeek },
|
|
39
|
+
{ unit: MONTH, step: 1, dt: durationMonth },
|
|
40
|
+
{ unit: MONTH, step: 3, dt: durationMonth * 3 },
|
|
41
|
+
{ unit: YEAR, step: 1, dt: durationYear }
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Determine a time interval for binning based on provided min
|
|
46
|
+
* and max timestamps and approximate step count.
|
|
47
|
+
* @param {Date|number} min The minimum timestamp value.
|
|
48
|
+
* @param {Date|number} max The maximum timestamp value.
|
|
49
|
+
* @param {number} steps The approximate number of bins desired.
|
|
50
|
+
* @returns {{ unit: TimeUnit, step: number }}
|
|
51
|
+
*/
|
|
52
|
+
export function timeInterval(min, max, steps) {
|
|
53
|
+
const span = +max - +min;
|
|
54
|
+
const t = span / steps; // target step size duration
|
|
55
|
+
const i = bisect(units, t, v => v.dt);
|
|
56
|
+
/** @type {TimeUnit} */
|
|
57
|
+
let unit;
|
|
58
|
+
let step;
|
|
59
|
+
|
|
60
|
+
if (i === units.length) {
|
|
61
|
+
unit = YEAR;
|
|
62
|
+
step = binStep(span / durationYear, steps);
|
|
63
|
+
} else if (i) {
|
|
64
|
+
({ unit, step } = units[t / units[i-1].dt < units[i].dt / t ? i-1 : i]);
|
|
65
|
+
} else {
|
|
66
|
+
step = binStep(span, steps);
|
|
67
|
+
unit = step >= 1 ? MILLISECOND : MICROSECOND;
|
|
68
|
+
step = step >= 1 ? step : step * 1000;
|
|
69
|
+
}
|
|
70
|
+
return { unit, step };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Perform a binary search.
|
|
75
|
+
* @template I, T
|
|
76
|
+
* @param {I[]} a The array to search.
|
|
77
|
+
* @param {T} x The target value
|
|
78
|
+
* @param {(item: I) => T} value A value accessor
|
|
79
|
+
* @returns {number} The search result array index.
|
|
80
|
+
*/
|
|
81
|
+
function bisect(a, x, value) {
|
|
82
|
+
const compare1 = (a, b) => a - b;
|
|
83
|
+
const compare2 = (d, x) => compare1(value(d), x);
|
|
84
|
+
|
|
85
|
+
let lo = 0;
|
|
86
|
+
let hi = a.length;
|
|
87
|
+
|
|
88
|
+
if (lo < hi) {
|
|
89
|
+
if (compare1(x, x) !== 0) return hi;
|
|
90
|
+
do {
|
|
91
|
+
const mid = (lo + hi) >>> 1;
|
|
92
|
+
if (compare2(a[mid], x) <= 0) lo = mid + 1;
|
|
93
|
+
else hi = mid;
|
|
94
|
+
} while (lo < hi);
|
|
95
|
+
}
|
|
96
|
+
return lo;
|
|
97
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -99,3 +99,14 @@ export type FromExpr = MaybeArray<FromEntry>;
|
|
|
99
99
|
export type FilterExpr = MaybeArray<string | ExprNode>;
|
|
100
100
|
export type GroupByExpr = MaybeArray<string | ExprNode>;
|
|
101
101
|
export type OrderByExpr = MaybeArray<string | ExprNode>;
|
|
102
|
+
|
|
103
|
+
export type TimeUnit =
|
|
104
|
+
| 'year'
|
|
105
|
+
| 'quarter'
|
|
106
|
+
| 'month'
|
|
107
|
+
| 'day'
|
|
108
|
+
| 'hour'
|
|
109
|
+
| 'minute'
|
|
110
|
+
| 'second'
|
|
111
|
+
| 'millisecond'
|
|
112
|
+
| 'microsecond';
|
package/src/util/ast.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ColumnRefNode } from '../ast/column-ref.js'
|
|
3
|
+
* @import { TableRefNode } from '../ast/table-ref.js'
|
|
4
|
+
*/
|
|
1
5
|
import { ExprNode } from '../ast/node.js';
|
|
2
6
|
import { ParamNode } from '../ast/param.js';
|
|
3
|
-
import { TableRefNode } from '../ast/table-ref.js';
|
|
4
7
|
import { WindowDefNode } from '../ast/window.js';
|
|
5
8
|
import { column } from '../functions/column.js';
|
|
6
9
|
import { literal, verbatim } from '../functions/literal.js';
|
|
@@ -68,7 +71,7 @@ export function asTableRef(value) {
|
|
|
68
71
|
* Parse a string as a column reference, potentially with
|
|
69
72
|
* dot ('.') delimited table, schema, and database references.
|
|
70
73
|
* @param {string} ref The column reference string.
|
|
71
|
-
* @returns {
|
|
74
|
+
* @returns {ColumnRefNode}
|
|
72
75
|
*/
|
|
73
76
|
export function parseColumnRef(ref) {
|
|
74
77
|
const ids = parseIdentifier(ref);
|
|
@@ -79,7 +82,7 @@ export function parseColumnRef(ref) {
|
|
|
79
82
|
* Parse a string as a table reference, potentially with
|
|
80
83
|
* dot ('.') delimited schema and database references.
|
|
81
84
|
* @param {string} ref The table reference string.
|
|
82
|
-
* @returns {
|
|
85
|
+
* @returns {TableRefNode}
|
|
83
86
|
*/
|
|
84
87
|
export function parseTableRef(ref) {
|
|
85
88
|
return tableRef(parseIdentifier(ref));
|
package/src/util/function.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { SQLNode } from '../ast/node.js'
|
|
3
|
+
* @import { WindowFunctionName } from '../types.js'
|
|
4
|
+
*/
|
|
1
5
|
import { AggregateNode } from '../ast/aggregate.js';
|
|
2
6
|
import { FunctionNode } from '../ast/function.js';
|
|
3
7
|
import { WindowFunctionNode, WindowNode } from '../ast/window.js';
|
|
@@ -5,7 +9,7 @@ import { asNode } from './ast.js';
|
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* Test if an AST node is a specific function call.
|
|
8
|
-
* @param {
|
|
12
|
+
* @param {SQLNode} node The SQL AST node to test.
|
|
9
13
|
* @param {string} name The function name.
|
|
10
14
|
* @returns {node is FunctionNode}
|
|
11
15
|
*/
|
|
@@ -37,7 +41,7 @@ export function aggFn(name, ...args) {
|
|
|
37
41
|
* Create a new window AST node. The output node has an empty window
|
|
38
42
|
* definition. Use chained calls such as `partitionby` and `orderby`
|
|
39
43
|
* to specify the window settings.
|
|
40
|
-
* @param {
|
|
44
|
+
* @param {WindowFunctionName} name The function name.
|
|
41
45
|
* @param {...any} args The function arguments.
|
|
42
46
|
* @returns {WindowNode}
|
|
43
47
|
*/
|
package/src/util/type-check.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ParamLike } from '../types.js'
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Check if a value is a string.
|
|
3
7
|
* @param {*} value The value to check.
|
|
@@ -19,7 +23,7 @@ export function isArray(value) {
|
|
|
19
23
|
/**
|
|
20
24
|
* Check if a value is a dynamic parameter.
|
|
21
25
|
* @param {*} value The value to check.
|
|
22
|
-
* @returns {value is
|
|
26
|
+
* @returns {value is ParamLike}
|
|
23
27
|
*/
|
|
24
28
|
export function isParamLike(value) {
|
|
25
29
|
return value
|
package/src/visit/visitors.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ParamNode } from '../ast/param.js'
|
|
3
|
+
* @import { ParamLike } from '../types.js'
|
|
4
|
+
*/
|
|
1
5
|
import { AGGREGATE, COLUMN_PARAM, COLUMN_REF, FRAGMENT, PARAM, VERBATIM, WINDOW } from '../constants.js';
|
|
2
6
|
import { aggregateNames, AggregateNode } from '../ast/aggregate.js';
|
|
3
7
|
import { ColumnRefNode } from '../ast/column-ref.js';
|
|
@@ -92,14 +96,14 @@ export function collectColumns(root) {
|
|
|
92
96
|
/**
|
|
93
97
|
* Collect all unique dynamic parameter instances.
|
|
94
98
|
* @param {SQLNode} root The root of the AST to search.
|
|
95
|
-
* @returns {
|
|
99
|
+
* @returns {ParamLike[]}
|
|
96
100
|
*/
|
|
97
101
|
export function collectParams(root) {
|
|
98
102
|
const params = new Set;
|
|
99
103
|
walk(root, (node) => {
|
|
100
104
|
if (node.type === PARAM) {
|
|
101
105
|
params.add(
|
|
102
|
-
/** @type {
|
|
106
|
+
/** @type {ParamNode} */
|
|
103
107
|
(node).param
|
|
104
108
|
);
|
|
105
109
|
}
|
package/src/visit/walk.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { SQLNode } from '../ast/node.js'
|
|
3
|
+
* @import { VisitorCallback, VisitorResult } from '../types.js'
|
|
4
|
+
|
|
5
|
+
*/
|
|
1
6
|
import { isNode } from '../ast/node.js';
|
|
2
7
|
import { recurse } from './recurse.js';
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* Perform a traversal of a SQL expression AST.
|
|
6
|
-
* @param {
|
|
7
|
-
* @param {
|
|
8
|
-
* @return {
|
|
11
|
+
* @param {SQLNode} node Root node for AST traversal.
|
|
12
|
+
* @param {VisitorCallback} visit Visitor callback function.
|
|
13
|
+
* @return {VisitorResult}
|
|
9
14
|
*/
|
|
10
15
|
export function walk(node, visit) {
|
|
11
16
|
if (!isNode(node)) return;
|