@uwdata/mosaic-core 0.9.0 → 0.11.0
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/mosaic-core.js +11493 -19779
- package/dist/mosaic-core.min.js +6 -15
- package/package.json +8 -8
- package/src/Coordinator.js +225 -55
- package/src/DataCubeIndexer.js +304 -141
- package/src/MosaicClient.js +12 -3
- package/src/QueryConsolidator.js +15 -11
- package/src/QueryManager.js +13 -5
- package/src/Selection.js +64 -12
- package/src/SelectionClause.js +22 -8
- package/src/connectors/rest.js +3 -3
- package/src/connectors/socket.js +4 -3
- package/src/connectors/wasm.js +20 -4
- package/src/index.js +10 -6
- package/src/util/AsyncDispatch.js +15 -5
- package/src/util/decode-ipc.js +11 -0
- package/src/util/field-info.js +3 -11
- package/src/util/index-columns.js +79 -80
- package/src/util/is-arrow-table.js +10 -0
- package/src/util/priority-queue.js +75 -76
- package/src/util/query-result.js +41 -8
- package/src/util/throttle.js +11 -1
- package/src/util/to-data-columns.js +60 -0
- package/src/FilterGroup.js +0 -81
- package/src/util/convert-arrow.js +0 -145
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
import { Query, agg, sql } from '@uwdata/mosaic-sql';
|
|
2
2
|
import { MosaicClient } from '../MosaicClient.js';
|
|
3
3
|
|
|
4
|
-
export const NO_INDEX = { from: NaN };
|
|
5
|
-
|
|
6
4
|
/**
|
|
7
5
|
* Determine data cube index columns for a given Mosaic client.
|
|
8
6
|
* @param {MosaicClient} client The Mosaic client.
|
|
9
7
|
* @returns An object with necessary column data to generate data
|
|
10
|
-
* cube index columns, null if
|
|
11
|
-
*
|
|
8
|
+
* cube index columns, or null if the client is not indexable or
|
|
9
|
+
* the client query contains an invalid or unsupported expression.
|
|
12
10
|
*/
|
|
13
11
|
export function indexColumns(client) {
|
|
14
|
-
if (!client.filterIndexable) return
|
|
12
|
+
if (!client.filterIndexable) return null;
|
|
15
13
|
const q = client.query();
|
|
16
|
-
const from =
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
const from = getBase(q, q => q.from()?.[0].from.table);
|
|
15
|
+
|
|
16
|
+
// bail if no base table or the query is not analyzable
|
|
17
|
+
if (typeof from !== 'string' || !q.select) return null;
|
|
19
18
|
|
|
20
19
|
const aggr = []; // list of output aggregate columns
|
|
21
20
|
const dims = []; // list of grouping dimension columns
|
|
22
21
|
const aux = {}; // auxiliary columns needed by aggregates
|
|
23
22
|
|
|
23
|
+
const avg = ref => {
|
|
24
|
+
const name = ref.column;
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
const expr = getBase(q, q => q.select().find(c => c.as === name)?.expr);
|
|
27
|
+
return `(SELECT AVG(${expr ?? ref}) FROM "${from}")`;
|
|
28
|
+
};
|
|
29
|
+
|
|
24
30
|
for (const entry of q.select()) {
|
|
25
31
|
const { as, expr: { aggregate, args } } = entry;
|
|
26
32
|
const op = aggregate?.toUpperCase?.();
|
|
@@ -47,32 +53,32 @@ export function indexColumns(client) {
|
|
|
47
53
|
case 'VARIANCE':
|
|
48
54
|
case 'VAR_SAMP':
|
|
49
55
|
aux[as] = null;
|
|
50
|
-
aggr.push({ [as]: varianceExpr(aux, args[0],
|
|
56
|
+
aggr.push({ [as]: varianceExpr(aux, args[0], avg) });
|
|
51
57
|
break;
|
|
52
58
|
case 'VAR_POP':
|
|
53
59
|
aux[as] = null;
|
|
54
|
-
aggr.push({ [as]: varianceExpr(aux, args[0],
|
|
60
|
+
aggr.push({ [as]: varianceExpr(aux, args[0], avg, false) });
|
|
55
61
|
break;
|
|
56
62
|
case 'STDDEV':
|
|
57
63
|
case 'STDDEV_SAMP':
|
|
58
64
|
aux[as] = null;
|
|
59
|
-
aggr.push({ [as]: agg`SQRT(${varianceExpr(aux, args[0],
|
|
65
|
+
aggr.push({ [as]: agg`SQRT(${varianceExpr(aux, args[0], avg)})` });
|
|
60
66
|
break;
|
|
61
67
|
case 'STDDEV_POP':
|
|
62
68
|
aux[as] = null;
|
|
63
|
-
aggr.push({ [as]: agg`SQRT(${varianceExpr(aux, args[0],
|
|
69
|
+
aggr.push({ [as]: agg`SQRT(${varianceExpr(aux, args[0], avg, false)})` });
|
|
64
70
|
break;
|
|
65
71
|
case 'COVAR_SAMP':
|
|
66
72
|
aux[as] = null;
|
|
67
|
-
aggr.push({ [as]: covarianceExpr(aux, args,
|
|
73
|
+
aggr.push({ [as]: covarianceExpr(aux, args, avg) });
|
|
68
74
|
break;
|
|
69
75
|
case 'COVAR_POP':
|
|
70
76
|
aux[as] = null;
|
|
71
|
-
aggr.push({ [as]: covarianceExpr(aux, args,
|
|
77
|
+
aggr.push({ [as]: covarianceExpr(aux, args, avg, false) });
|
|
72
78
|
break;
|
|
73
79
|
case 'CORR':
|
|
74
80
|
aux[as] = null;
|
|
75
|
-
aggr.push({ [as]: corrExpr(aux, args,
|
|
81
|
+
aggr.push({ [as]: corrExpr(aux, args, avg) });
|
|
76
82
|
break;
|
|
77
83
|
|
|
78
84
|
// regression statistics
|
|
@@ -90,27 +96,27 @@ export function indexColumns(client) {
|
|
|
90
96
|
break;
|
|
91
97
|
case 'REGR_SYY':
|
|
92
98
|
aux[as] = null;
|
|
93
|
-
aggr.push({ [as]: regrVarExpr(aux, 0, args,
|
|
99
|
+
aggr.push({ [as]: regrVarExpr(aux, 0, args, avg) });
|
|
94
100
|
break;
|
|
95
101
|
case 'REGR_SXX':
|
|
96
102
|
aux[as] = null;
|
|
97
|
-
aggr.push({ [as]: regrVarExpr(aux, 1, args,
|
|
103
|
+
aggr.push({ [as]: regrVarExpr(aux, 1, args, avg) });
|
|
98
104
|
break;
|
|
99
105
|
case 'REGR_SXY':
|
|
100
106
|
aux[as] = null;
|
|
101
|
-
aggr.push({ [as]: covarianceExpr(aux, args,
|
|
107
|
+
aggr.push({ [as]: covarianceExpr(aux, args, avg, null) });
|
|
102
108
|
break;
|
|
103
109
|
case 'REGR_SLOPE':
|
|
104
110
|
aux[as] = null;
|
|
105
|
-
aggr.push({ [as]: regrSlopeExpr(aux, args,
|
|
111
|
+
aggr.push({ [as]: regrSlopeExpr(aux, args, avg) });
|
|
106
112
|
break;
|
|
107
113
|
case 'REGR_INTERCEPT':
|
|
108
114
|
aux[as] = null;
|
|
109
|
-
aggr.push({ [as]: regrInterceptExpr(aux, args,
|
|
115
|
+
aggr.push({ [as]: regrInterceptExpr(aux, args, avg) });
|
|
110
116
|
break;
|
|
111
117
|
case 'REGR_R2':
|
|
112
118
|
aux[as] = null;
|
|
113
|
-
aggr.push({ [as]: agg`(${corrExpr(aux, args,
|
|
119
|
+
aggr.push({ [as]: agg`(${corrExpr(aux, args, avg)}) ** 2` });
|
|
114
120
|
break;
|
|
115
121
|
|
|
116
122
|
// aggregates that commute directly
|
|
@@ -127,11 +133,14 @@ export function indexColumns(client) {
|
|
|
127
133
|
|
|
128
134
|
// otherwise, check if dimension
|
|
129
135
|
default:
|
|
130
|
-
if (
|
|
136
|
+
if (!aggregate) dims.push(as);
|
|
131
137
|
else return null; // unsupported aggregate
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
|
|
141
|
+
// bail if the query has no aggregates
|
|
142
|
+
if (!aggr.length) return null;
|
|
143
|
+
|
|
135
144
|
return { from, dims, aggr, aux };
|
|
136
145
|
}
|
|
137
146
|
|
|
@@ -161,29 +170,30 @@ function sanitize(col) {
|
|
|
161
170
|
}
|
|
162
171
|
|
|
163
172
|
/**
|
|
164
|
-
* Identify a
|
|
173
|
+
* Identify a shared base (source) query and extract a value from it.
|
|
174
|
+
* This method is used to find a shared base table name or extract
|
|
175
|
+
* the original column name within a base table.
|
|
165
176
|
* @param {Query} query The input query.
|
|
166
|
-
* @
|
|
177
|
+
* @param {(q: Query) => any} get A getter function to extract
|
|
178
|
+
* a value from a base query.
|
|
179
|
+
* @returns {string | undefined | NaN} the base query value, or
|
|
167
180
|
* `undefined` if there is no source table, or `NaN` if the
|
|
168
181
|
* query operates over multiple source tables.
|
|
169
182
|
*/
|
|
170
|
-
function
|
|
183
|
+
function getBase(query, get) {
|
|
171
184
|
const subq = query.subqueries;
|
|
172
185
|
|
|
173
186
|
// select query
|
|
174
|
-
if (query.select) {
|
|
175
|
-
|
|
176
|
-
// @ts-ignore
|
|
177
|
-
if (!from.length) return undefined;
|
|
178
|
-
if (subq.length === 0) return from[0].from.table;
|
|
187
|
+
if (query.select && subq.length === 0) {
|
|
188
|
+
return get(query);
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
// handle set operations / subqueries
|
|
182
|
-
const base =
|
|
192
|
+
const base = getBase(subq[0], get);
|
|
183
193
|
for (let i = 1; i < subq.length; ++i) {
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
186
|
-
if (
|
|
194
|
+
const value = getBase(subq[i], get);
|
|
195
|
+
if (value === undefined) continue;
|
|
196
|
+
if (value !== base) return NaN;
|
|
187
197
|
}
|
|
188
198
|
return base;
|
|
189
199
|
}
|
|
@@ -222,17 +232,6 @@ function avgExpr(aux, as, arg) {
|
|
|
222
232
|
return agg`(SUM("${as}" * ${n.name}) / ${n})`;
|
|
223
233
|
}
|
|
224
234
|
|
|
225
|
-
/**
|
|
226
|
-
* Generate a scalar subquery for a global average.
|
|
227
|
-
* This value can be used to mean-center data.
|
|
228
|
-
* @param {*} x Souce data table column.
|
|
229
|
-
* @param {string} from The source data table name.
|
|
230
|
-
* @returns A scalar aggregate query
|
|
231
|
-
*/
|
|
232
|
-
function avg(x, from) {
|
|
233
|
-
return sql`(SELECT AVG(${x}) FROM "${from}")`;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
235
|
/**
|
|
237
236
|
* Generate an expression for calculating argmax over data partitions.
|
|
238
237
|
* As a side effect, this method adds a column to the input *aux* object
|
|
@@ -281,18 +280,18 @@ function argminExpr(aux, as, [, y]) {
|
|
|
281
280
|
* sufficient statistics) to include in the data cube aggregation.
|
|
282
281
|
* @param {*} x The source data table column. This may be a string,
|
|
283
282
|
* column reference, SQL expression, or other string-coercible value.
|
|
284
|
-
* @param {string}
|
|
283
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
285
284
|
* @param {boolean} [correction=true] A flag for whether a Bessel
|
|
286
285
|
* correction should be applied to compute the sample variance
|
|
287
286
|
* rather than the populatation variance.
|
|
288
287
|
* @returns An aggregate expression for calculating variance over
|
|
289
288
|
* pre-aggregated data partitions.
|
|
290
289
|
*/
|
|
291
|
-
function varianceExpr(aux, x,
|
|
290
|
+
function varianceExpr(aux, x, avg, correction = true) {
|
|
292
291
|
const n = countExpr(aux, x);
|
|
293
292
|
const ssq = auxName('rssq', x); // residual sum of squares
|
|
294
293
|
const sum = auxName('rsum', x); // residual sum
|
|
295
|
-
const delta = sql`${x} - ${avg(x
|
|
294
|
+
const delta = sql`${x} - ${avg(x)}`;
|
|
296
295
|
aux[ssq] = agg`SUM((${delta}) ** 2)`;
|
|
297
296
|
aux[sum] = agg`SUM(${delta})`;
|
|
298
297
|
const adj = correction ? ` - 1` : ''; // Bessel correction
|
|
@@ -310,7 +309,7 @@ function varianceExpr(aux, x, from, correction = true) {
|
|
|
310
309
|
* sufficient statistics) to include in the data cube aggregation.
|
|
311
310
|
* @param {any[]} args Source data table columns. The entries may be strings,
|
|
312
311
|
* column references, SQL expressions, or other string-coercible values.
|
|
313
|
-
* @param {string}
|
|
312
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
314
313
|
* @param {boolean|null} [correction=true] A flag for whether a Bessel
|
|
315
314
|
* correction should be applied to compute the sample covariance rather
|
|
316
315
|
* than the populatation covariance. If null, an expression for the
|
|
@@ -318,11 +317,11 @@ function varianceExpr(aux, x, from, correction = true) {
|
|
|
318
317
|
* @returns An aggregate expression for calculating covariance over
|
|
319
318
|
* pre-aggregated data partitions.
|
|
320
319
|
*/
|
|
321
|
-
function covarianceExpr(aux, args,
|
|
320
|
+
function covarianceExpr(aux, args, avg, correction = true) {
|
|
322
321
|
const n = regrCountExpr(aux, args);
|
|
323
|
-
const sxy = regrSumXYExpr(aux, args,
|
|
324
|
-
const sx = regrSumExpr(aux, 1, args,
|
|
325
|
-
const sy = regrSumExpr(aux, 0, args,
|
|
322
|
+
const sxy = regrSumXYExpr(aux, args, avg);
|
|
323
|
+
const sx = regrSumExpr(aux, 1, args, avg);
|
|
324
|
+
const sy = regrSumExpr(aux, 0, args, avg);
|
|
326
325
|
const adj = correction === null ? '' // do not divide by count
|
|
327
326
|
: correction ? ` / (${n} - 1)` // Bessel correction (sample)
|
|
328
327
|
: ` / ${n}`; // no correction (population)
|
|
@@ -341,17 +340,17 @@ function covarianceExpr(aux, args, from, correction = true) {
|
|
|
341
340
|
* sufficient statistics) to include in the data cube aggregation.
|
|
342
341
|
* @param {any[]} args Source data table columns. The entries may be strings,
|
|
343
342
|
* column references, SQL expressions, or other string-coercible values.
|
|
344
|
-
* @param {string}
|
|
343
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
345
344
|
* @returns An aggregate expression for calculating correlation over
|
|
346
345
|
* pre-aggregated data partitions.
|
|
347
346
|
*/
|
|
348
|
-
function corrExpr(aux, args,
|
|
347
|
+
function corrExpr(aux, args, avg) {
|
|
349
348
|
const n = regrCountExpr(aux, args);
|
|
350
|
-
const sxy = regrSumXYExpr(aux, args,
|
|
351
|
-
const sxx = regrSumSqExpr(aux, 1, args,
|
|
352
|
-
const syy = regrSumSqExpr(aux, 0, args,
|
|
353
|
-
const sx = regrSumExpr(aux, 1, args,
|
|
354
|
-
const sy = regrSumExpr(aux, 0, args,
|
|
349
|
+
const sxy = regrSumXYExpr(aux, args, avg);
|
|
350
|
+
const sxx = regrSumSqExpr(aux, 1, args, avg);
|
|
351
|
+
const syy = regrSumSqExpr(aux, 0, args, avg);
|
|
352
|
+
const sx = regrSumExpr(aux, 1, args, avg);
|
|
353
|
+
const sy = regrSumExpr(aux, 0, args, avg);
|
|
355
354
|
const vx = agg`(${sxx} - (${sx} ** 2) / ${n})`;
|
|
356
355
|
const vy = agg`(${syy} - (${sy} ** 2) / ${n})`;
|
|
357
356
|
return agg`(${sxy} - ${sx} * ${sy} / ${n}) / SQRT(${vx} * ${vy})`;
|
|
@@ -385,14 +384,14 @@ function regrCountExpr(aux, [y, x]) {
|
|
|
385
384
|
* @param {number} i An index indicating which argument column to sum.
|
|
386
385
|
* @param {any[]} args Source data table columns. The entries may be strings,
|
|
387
386
|
* column references, SQL expressions, or other string-coercible values.
|
|
388
|
-
* @param {string}
|
|
387
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
389
388
|
* @returns An aggregate expression over pre-aggregated data partitions.
|
|
390
389
|
*/
|
|
391
|
-
function regrSumExpr(aux, i, args,
|
|
390
|
+
function regrSumExpr(aux, i, args, avg) {
|
|
392
391
|
const v = args[i];
|
|
393
392
|
const o = args[1 - i];
|
|
394
393
|
const sum = auxName('rs', v);
|
|
395
|
-
aux[sum] = agg`SUM(${v} - ${avg(v
|
|
394
|
+
aux[sum] = agg`SUM(${v} - ${avg(v)}) FILTER (${o} IS NOT NULL)`;
|
|
396
395
|
return agg`SUM(${sum})`
|
|
397
396
|
}
|
|
398
397
|
|
|
@@ -407,14 +406,14 @@ function regrSumExpr(aux, i, args, from) {
|
|
|
407
406
|
* @param {number} i An index indicating which argument column to sum.
|
|
408
407
|
* @param {any[]} args Source data table columns. The entries may be strings,
|
|
409
408
|
* column references, SQL expressions, or other string-coercible values.
|
|
410
|
-
* @param {string}
|
|
409
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
411
410
|
* @returns An aggregate expression over pre-aggregated data partitions.
|
|
412
411
|
*/
|
|
413
|
-
function regrSumSqExpr(aux, i, args,
|
|
412
|
+
function regrSumSqExpr(aux, i, args, avg) {
|
|
414
413
|
const v = args[i];
|
|
415
414
|
const u = args[1 - i];
|
|
416
415
|
const ssq = auxName('rss', v);
|
|
417
|
-
aux[ssq] = agg`SUM((${v} - ${avg(v
|
|
416
|
+
aux[ssq] = agg`SUM((${v} - ${avg(v)}) ** 2) FILTER (${u} IS NOT NULL)`;
|
|
418
417
|
return agg`SUM(${ssq})`
|
|
419
418
|
}
|
|
420
419
|
|
|
@@ -428,13 +427,13 @@ function regrSumSqExpr(aux, i, args, from) {
|
|
|
428
427
|
* sufficient statistics) to include in the data cube aggregation.
|
|
429
428
|
* @param {any[]} args Source data table columns. The entries may be strings,
|
|
430
429
|
* column references, SQL expressions, or other string-coercible values.
|
|
431
|
-
* @param {string}
|
|
430
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
432
431
|
* @returns An aggregate expression over pre-aggregated data partitions.
|
|
433
432
|
*/
|
|
434
|
-
function regrSumXYExpr(aux, args,
|
|
433
|
+
function regrSumXYExpr(aux, args, avg) {
|
|
435
434
|
const [y, x] = args;
|
|
436
435
|
const sxy = auxName('sxy', y, x);
|
|
437
|
-
aux[sxy] = agg`SUM((${x} - ${avg(x
|
|
436
|
+
aux[sxy] = agg`SUM((${x} - ${avg(x)}) * (${y} - ${avg(y)}))`;
|
|
438
437
|
return agg`SUM(${sxy})`;
|
|
439
438
|
}
|
|
440
439
|
|
|
@@ -487,14 +486,14 @@ function regrAvgYExpr(aux, args) {
|
|
|
487
486
|
* @param {number} i The index of the argument to compute the variance for.
|
|
488
487
|
* @param {any[]} args Source data table columns. The entries may be strings,
|
|
489
488
|
* column references, SQL expressions, or other string-coercible values.
|
|
490
|
-
* @param {string}
|
|
489
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
491
490
|
* @returns An aggregate expression for calculating variance over
|
|
492
491
|
* pre-aggregated data partitions.
|
|
493
492
|
*/
|
|
494
|
-
function regrVarExpr(aux, i, args,
|
|
493
|
+
function regrVarExpr(aux, i, args, avg) {
|
|
495
494
|
const n = regrCountExpr(aux, args);
|
|
496
|
-
const sum = regrSumExpr(aux, i, args,
|
|
497
|
-
const ssq = regrSumSqExpr(aux, i, args,
|
|
495
|
+
const sum = regrSumExpr(aux, i, args, avg);
|
|
496
|
+
const ssq = regrSumSqExpr(aux, i, args, avg);
|
|
498
497
|
return agg`(${ssq} - (${sum} ** 2 / ${n}))`;
|
|
499
498
|
}
|
|
500
499
|
|
|
@@ -507,13 +506,13 @@ function regrVarExpr(aux, i, args, from) {
|
|
|
507
506
|
* sufficient statistics) to include in the data cube aggregation.
|
|
508
507
|
* @param {any[]} args Source data table columns. The entries may be strings,
|
|
509
508
|
* column references, SQL expressions, or other string-coercible values.
|
|
510
|
-
* @param {string}
|
|
509
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
511
510
|
* @returns An aggregate expression for calculating regression slopes over
|
|
512
511
|
* pre-aggregated data partitions.
|
|
513
512
|
*/
|
|
514
|
-
function regrSlopeExpr(aux, args,
|
|
515
|
-
const cov = covarianceExpr(aux, args,
|
|
516
|
-
const varx = regrVarExpr(aux, 1, args,
|
|
513
|
+
function regrSlopeExpr(aux, args, avg) {
|
|
514
|
+
const cov = covarianceExpr(aux, args, avg, null);
|
|
515
|
+
const varx = regrVarExpr(aux, 1, args, avg);
|
|
517
516
|
return agg`(${cov}) / ${varx}`;
|
|
518
517
|
}
|
|
519
518
|
|
|
@@ -526,13 +525,13 @@ function regrSlopeExpr(aux, args, from) {
|
|
|
526
525
|
* sufficient statistics) to include in the data cube aggregation.
|
|
527
526
|
* @param {any[]} args Source data table columns. The entries may be strings,
|
|
528
527
|
* column references, SQL expressions, or other string-coercible values.
|
|
529
|
-
* @param {string}
|
|
528
|
+
* @param {(field: any) => string} avg Global average query generator.
|
|
530
529
|
* @returns An aggregate expression for calculating regression intercepts over
|
|
531
530
|
* pre-aggregated data partitions.
|
|
532
531
|
*/
|
|
533
|
-
function regrInterceptExpr(aux, args,
|
|
532
|
+
function regrInterceptExpr(aux, args, avg) {
|
|
534
533
|
const ax = regrAvgXExpr(aux, args);
|
|
535
534
|
const ay = regrAvgYExpr(aux, args);
|
|
536
|
-
const m = regrSlopeExpr(aux, args,
|
|
535
|
+
const m = regrSlopeExpr(aux, args, avg);
|
|
537
536
|
return agg`${ay} - (${m}) * ${ax}`;
|
|
538
537
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test if a value is a Flechette Arrow table.
|
|
3
|
+
* We use a "duck typing" approach and check for a getChild function.
|
|
4
|
+
* @param {*} values The value to test
|
|
5
|
+
* @returns {values is import('@uwdata/flechette').Table}
|
|
6
|
+
* true if the value duck types as Arrow data
|
|
7
|
+
*/
|
|
8
|
+
export function isArrowTable(values) {
|
|
9
|
+
return typeof values?.getChild === 'function';
|
|
10
|
+
}
|
|
@@ -1,85 +1,84 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
export class PriorityQueue {
|
|
2
|
+
/**
|
|
3
|
+
* Create a new priority queue instance.
|
|
4
|
+
* @param {number} ranks An integer number of rank-order priority levels.
|
|
5
|
+
*/
|
|
6
|
+
constructor(ranks) {
|
|
7
|
+
// one list for each integer priority level
|
|
8
|
+
this.queue = Array.from(
|
|
9
9
|
{ length: ranks },
|
|
10
10
|
() => ({ head: null, tail: null })
|
|
11
11
|
);
|
|
12
|
+
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
14
|
+
/**
|
|
15
|
+
* Indicate if the queue is empty.
|
|
16
|
+
* @returns {boolean} true if empty, false otherwise.
|
|
17
|
+
*/
|
|
18
|
+
isEmpty() {
|
|
19
|
+
return this.queue.every(list => !list.head);
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Insert an item into the queue with a given priority rank.
|
|
24
|
+
* @param {*} item The item to add.
|
|
25
|
+
* @param {number} rank The integer priority rank.
|
|
26
|
+
* Priority ranks are integers starting at zero.
|
|
27
|
+
* Lower ranks indicate higher priority.
|
|
28
|
+
*/
|
|
29
|
+
insert(item, rank) {
|
|
30
|
+
const list = this.queue[rank];
|
|
31
|
+
if (!list) {
|
|
32
|
+
throw new Error(`Invalid queue priority rank: ${rank}`);
|
|
33
|
+
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
const node = { item, next: null };
|
|
36
|
+
if (list.head === null) {
|
|
37
|
+
list.head = list.tail = node;
|
|
38
|
+
} else {
|
|
39
|
+
list.tail = list.tail.next = node;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Remove a set of items from the queue, regardless of priority rank.
|
|
45
|
+
* If a provided item is not in the queue it will be ignored.
|
|
46
|
+
* @param {(item: *) => boolean} test A predicate function to test
|
|
47
|
+
* if an item should be removed (true to drop, false to keep).
|
|
48
|
+
*/
|
|
49
|
+
remove(test) {
|
|
50
|
+
for (const list of this.queue) {
|
|
51
|
+
let { head, tail } = list;
|
|
52
|
+
for (let prev = null, curr = head; curr; prev = curr, curr = curr.next) {
|
|
53
|
+
if (test(curr.item)) {
|
|
54
|
+
if (curr === head) {
|
|
55
|
+
head = curr.next;
|
|
56
|
+
} else {
|
|
57
|
+
prev.next = curr.next;
|
|
58
|
+
}
|
|
59
|
+
if (curr === tail) tail = prev || head;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
list.head = head;
|
|
63
|
+
list.tail = tail;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
};
|
|
67
|
+
/**
|
|
68
|
+
* Remove and return the next highest priority item.
|
|
69
|
+
* @returns {*} The next item in the queue,
|
|
70
|
+
* or undefined if this queue is empty.
|
|
71
|
+
*/
|
|
72
|
+
next() {
|
|
73
|
+
for (const list of this.queue) {
|
|
74
|
+
const { head } = list;
|
|
75
|
+
if (head !== null) {
|
|
76
|
+
list.head = head.next;
|
|
77
|
+
if (list.tail === head) {
|
|
78
|
+
list.tail = null;
|
|
79
|
+
}
|
|
80
|
+
return head.item;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
85
84
|
}
|
package/src/util/query-result.js
CHANGED
|
@@ -1,9 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A query result Promise that can allows external callers
|
|
3
|
+
* to resolve or reject the Promise.
|
|
4
|
+
*/
|
|
5
|
+
export class QueryResult extends Promise {
|
|
6
|
+
/**
|
|
7
|
+
* Create a new query result Promise.
|
|
8
|
+
*/
|
|
9
|
+
constructor() {
|
|
10
|
+
let resolve;
|
|
11
|
+
let reject;
|
|
12
|
+
super((r, e) => {
|
|
13
|
+
resolve = r;
|
|
14
|
+
reject = e;
|
|
15
|
+
});
|
|
16
|
+
this._resolve = resolve;
|
|
17
|
+
this._reject = reject;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the result Promise with the provided value.
|
|
22
|
+
* @param {*} value The result value.
|
|
23
|
+
* @returns {this}
|
|
24
|
+
*/
|
|
25
|
+
fulfill(value) {
|
|
26
|
+
this._resolve(value);
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Rejects the result Promise with the provided error.
|
|
32
|
+
* @param {*} error The error value.
|
|
33
|
+
* @returns {this}
|
|
34
|
+
*/
|
|
35
|
+
reject(error) {
|
|
36
|
+
this._reject(error);
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
9
39
|
}
|
|
40
|
+
|
|
41
|
+
// necessary to make Promise subclass act like a Promise
|
|
42
|
+
QueryResult.prototype.constructor = Promise;
|
package/src/util/throttle.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
const NIL = {};
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Throttle invocations of a callback function. The callback must return
|
|
5
|
+
* a Promise. Upon repeated invocation, the callback will not be invoked
|
|
6
|
+
* until a prior Promise resolves. If multiple invocations occurs while
|
|
7
|
+
* waiting, only the most recent invocation will be pending.
|
|
8
|
+
* @param {(event: *) => Promise} callback The callback function.
|
|
9
|
+
* @param {boolean} [debounce=true] Flag indicating if invocations
|
|
10
|
+
* should also be debounced within the current animation frame.
|
|
11
|
+
* @returns A new function that throttles access to the callback.
|
|
12
|
+
*/
|
|
3
13
|
export function throttle(callback, debounce = false) {
|
|
4
14
|
let curr;
|
|
5
15
|
let next;
|
|
6
16
|
let pending = NIL;
|
|
7
17
|
|
|
8
18
|
function invoke(event) {
|
|
9
|
-
curr = callback(event).
|
|
19
|
+
curr = callback(event).finally(() => {
|
|
10
20
|
if (next) {
|
|
11
21
|
const { value } = next;
|
|
12
22
|
next = null;
|