@uwdata/mosaic-core 0.12.1 → 0.13.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/types/Coordinator.d.ts +19 -19
- package/dist/types/MosaicClient.d.ts +25 -10
- package/dist/types/QueryManager.d.ts +3 -1
- package/dist/types/SelectionClause.d.ts +8 -8
- package/dist/types/index-types.d.ts +3 -0
- package/dist/types/index.d.ts +4 -10
- package/dist/types/make-client.d.ts +48 -0
- package/dist/types/types.d.ts +42 -0
- package/dist/types/util/field-info.d.ts +11 -1
- package/dist/types/util/is-activatable.d.ts +6 -0
- package/dist/types/util/js-type.d.ts +7 -1
- package/dist/types/util/throttle.d.ts +5 -3
- package/package.json +12 -12
- package/src/Coordinator.js +18 -16
- package/src/MosaicClient.js +23 -3
- package/src/QueryManager.js +3 -1
- package/src/SelectionClause.js +4 -6
- package/src/connectors/rest.js +9 -2
- package/src/index-types.ts +3 -0
- package/src/index.js +4 -13
- package/src/make-client.js +64 -0
- package/src/preagg/sufficient-statistics.js +21 -1
- package/src/types.ts +64 -0
- package/src/util/field-info.js +40 -14
- package/src/util/is-activatable.js +8 -0
- package/src/util/js-type.js +6 -0
- package/src/util/throttle.js +4 -2
- package/vitest.config.ts +3 -0
- package/dist/mosaic-core.js +0 -18782
- package/dist/mosaic-core.min.js +0 -11
package/src/MosaicClient.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Coordinator } from './Coordinator.js';
|
|
2
|
+
import { Selection } from './Selection.js';
|
|
1
3
|
import { throttle } from './util/throttle.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -11,9 +13,13 @@ export class MosaicClient {
|
|
|
11
13
|
* the client when the selection updates.
|
|
12
14
|
*/
|
|
13
15
|
constructor(filterSelection) {
|
|
16
|
+
/** @type {Selection} */
|
|
14
17
|
this._filterBy = filterSelection;
|
|
15
18
|
this._requestUpdate = throttle(() => this.requestQuery(), true);
|
|
19
|
+
/** @type {Coordinator} */
|
|
16
20
|
this._coordinator = null;
|
|
21
|
+
/** @type {Promise<any>} */
|
|
22
|
+
this._pending = Promise.resolve();
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
/**
|
|
@@ -30,6 +36,13 @@ export class MosaicClient {
|
|
|
30
36
|
this._coordinator = coordinator;
|
|
31
37
|
}
|
|
32
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Return a Promise that resolves once the client has updated.
|
|
41
|
+
*/
|
|
42
|
+
get pending() {
|
|
43
|
+
return this._pending;
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
/**
|
|
34
47
|
* Return this client's filter selection.
|
|
35
48
|
*/
|
|
@@ -49,7 +62,8 @@ export class MosaicClient {
|
|
|
49
62
|
|
|
50
63
|
/**
|
|
51
64
|
* Return an array of fields queried by this client.
|
|
52
|
-
* @returns {
|
|
65
|
+
* @returns {import('./types.js').FieldInfoRequest[] | null}
|
|
66
|
+
* The fields to retrieve info for.
|
|
53
67
|
*/
|
|
54
68
|
fields() {
|
|
55
69
|
return null;
|
|
@@ -57,13 +71,19 @@ export class MosaicClient {
|
|
|
57
71
|
|
|
58
72
|
/**
|
|
59
73
|
* Called by the coordinator to set the field info for this client.
|
|
60
|
-
* @param {
|
|
74
|
+
* @param {import('./types.js').FieldInfo[]} info The field info result.
|
|
61
75
|
* @returns {this}
|
|
62
76
|
*/
|
|
63
77
|
fieldInfo(info) { // eslint-disable-line no-unused-vars
|
|
64
78
|
return this;
|
|
65
79
|
}
|
|
66
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Prepare the client before the query() method is called.
|
|
83
|
+
*/
|
|
84
|
+
async prepare() {
|
|
85
|
+
}
|
|
86
|
+
|
|
67
87
|
/**
|
|
68
88
|
* Return a query specifying the data needed by this client.
|
|
69
89
|
* @param {*} [filter] The filtering criteria to apply in the query.
|
|
@@ -122,7 +142,7 @@ export class MosaicClient {
|
|
|
122
142
|
}
|
|
123
143
|
|
|
124
144
|
/**
|
|
125
|
-
* Reset this client, initiating new field info and query requests.
|
|
145
|
+
* Reset this client, initiating new field info, call the prepare method, and query requests.
|
|
126
146
|
* @returns {Promise}
|
|
127
147
|
*/
|
|
128
148
|
initialize() {
|
package/src/QueryManager.js
CHANGED
|
@@ -10,6 +10,7 @@ export class QueryManager {
|
|
|
10
10
|
constructor(
|
|
11
11
|
maxConcurrentRequests = 32
|
|
12
12
|
) {
|
|
13
|
+
/** @type {PriorityQueue} */
|
|
13
14
|
this.queue = new PriorityQueue(3);
|
|
14
15
|
this.db = null;
|
|
15
16
|
this.clientCache = null;
|
|
@@ -18,11 +19,12 @@ export class QueryManager {
|
|
|
18
19
|
this._consolidate = null;
|
|
19
20
|
/**
|
|
20
21
|
* Requests pending with the query manager.
|
|
21
|
-
*
|
|
22
22
|
* @type {QueryResult[]}
|
|
23
23
|
*/
|
|
24
24
|
this.pendingResults = [];
|
|
25
|
+
/** @type {number} */
|
|
25
26
|
this.maxConcurrentRequests = maxConcurrentRequests;
|
|
27
|
+
/** @type {boolean} */
|
|
26
28
|
this.pendingExec = false;
|
|
27
29
|
}
|
|
28
30
|
|
package/src/SelectionClause.js
CHANGED
|
@@ -40,7 +40,7 @@ export function clausePoint(field, value, {
|
|
|
40
40
|
/**
|
|
41
41
|
* Generate a selection clause for multiple selected point values.
|
|
42
42
|
* @param {import('@uwdata/mosaic-sql').ExprValue[]} fields The table columns or expressions to select.
|
|
43
|
-
* @param {any[][] | undefined} value The selected values, as an array of
|
|
43
|
+
* @param {any[][] | null | undefined} value The selected values, as an array of
|
|
44
44
|
* arrays. Each subarray contains values for each *fields* entry.
|
|
45
45
|
* @param {object} options Additional clause properties.
|
|
46
46
|
* @param {*} options.source The source component generating this clause.
|
|
@@ -75,7 +75,7 @@ export function clausePoints(fields, value, {
|
|
|
75
75
|
/**
|
|
76
76
|
* Generate a selection clause for a selected 1D interval.
|
|
77
77
|
* @param {import('@uwdata/mosaic-sql').ExprValue} field The table column or expression to select.
|
|
78
|
-
* @param {Extent} value The selected interval as a [lo, hi] array.
|
|
78
|
+
* @param {Extent | null | undefined} value The selected interval as a [lo, hi] array.
|
|
79
79
|
* @param {object} options Additional clause properties.
|
|
80
80
|
* @param {*} options.source The source component generating this clause.
|
|
81
81
|
* @param {Set<MosaicClient>} [options.clients] The Mosaic clients associated
|
|
@@ -93,7 +93,6 @@ export function clauseInterval(field, value, {
|
|
|
93
93
|
scale,
|
|
94
94
|
pixelSize = 1
|
|
95
95
|
}) {
|
|
96
|
-
/** @type {ExprNode | null} */
|
|
97
96
|
const predicate = value != null ? isBetween(field, value) : null;
|
|
98
97
|
/** @type {import('./util/selection-types.js').IntervalMetadata} */
|
|
99
98
|
const meta = { type: 'interval', scales: scale && [scale], bin, pixelSize };
|
|
@@ -103,7 +102,7 @@ export function clauseInterval(field, value, {
|
|
|
103
102
|
/**
|
|
104
103
|
* Generate a selection clause for multiple selected intervals.
|
|
105
104
|
* @param {import('@uwdata/mosaic-sql').ExprValue[]} fields The table columns or expressions to select.
|
|
106
|
-
* @param {Extent[]} value The selected intervals, as an array of extents.
|
|
105
|
+
* @param {Extent[] | null | undefined} value The selected intervals, as an array of extents.
|
|
107
106
|
* @param {object} options Additional clause properties.
|
|
108
107
|
* @param {*} options.source The source component generating this clause.
|
|
109
108
|
* @param {Set<MosaicClient>} [options.clients] The Mosaic clients associated
|
|
@@ -122,7 +121,6 @@ export function clauseIntervals(fields, value, {
|
|
|
122
121
|
scales = [],
|
|
123
122
|
pixelSize = 1
|
|
124
123
|
}) {
|
|
125
|
-
/** @type {ExprNode | null} */
|
|
126
124
|
const predicate = value != null
|
|
127
125
|
? and(fields.map((f, i) => isBetween(f, value[i])))
|
|
128
126
|
: null;
|
|
@@ -136,7 +134,7 @@ const MATCH_METHODS = { contains, prefix, suffix, regexp: regexp_matches };
|
|
|
136
134
|
/**
|
|
137
135
|
* Generate a selection clause for text search matching.
|
|
138
136
|
* @param {import('@uwdata/mosaic-sql').ExprValue} field The table column or expression to select.
|
|
139
|
-
* @param {string} value The selected text search query string.
|
|
137
|
+
* @param {string | null | undefined} value The selected text search query string.
|
|
140
138
|
* @param {object} options Additional clause properties.
|
|
141
139
|
* @param {*} options.source The source component generating this clause.
|
|
142
140
|
* @param {Set<MosaicClient>} [options.clients] The Mosaic clients associated
|
package/src/connectors/rest.js
CHANGED
|
@@ -21,9 +21,16 @@ export function restConnector(uri = 'http://localhost:3000/') {
|
|
|
21
21
|
body: JSON.stringify(query)
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
const res = await req;
|
|
26
|
+
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
throw new Error(`Query failed with HTTP status ${res.status}: ${await res.text()}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
return query.type === 'exec' ? req
|
|
25
|
-
: query.type === 'arrow' ? decodeIPC(await
|
|
26
|
-
:
|
|
32
|
+
: query.type === 'arrow' ? decodeIPC(await res.arrayBuffer())
|
|
33
|
+
: res.json();
|
|
27
34
|
}
|
|
28
35
|
};
|
|
29
36
|
}
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { MosaicClient } from './MosaicClient.js';
|
|
2
|
+
export { makeClient } from './make-client.js';
|
|
2
3
|
export { Coordinator, coordinator } from './Coordinator.js';
|
|
3
4
|
export { Selection, isSelection } from './Selection.js';
|
|
4
5
|
export { Param, isParam } from './Param.js';
|
|
@@ -22,16 +23,6 @@ export { isArrowTable } from './util/is-arrow-table.js';
|
|
|
22
23
|
export { synchronizer } from './util/synchronizer.js';
|
|
23
24
|
export { throttle } from './util/throttle.js';
|
|
24
25
|
export { toDataColumns } from './util/to-data-columns.js';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* @typedef {import('./util/selection-types.js').PointMetadata} PointMetadata
|
|
29
|
-
* @typedef {import('./util/selection-types.js').MatchMethod} MatchMethod
|
|
30
|
-
* @typedef {import('./util/selection-types.js').MatchMetadata} MatchMetadata
|
|
31
|
-
* @typedef {import('./util/selection-types.js').ScaleType} ScaleType
|
|
32
|
-
* @typedef {import('./util/selection-types.js').Extent} Extent
|
|
33
|
-
* @typedef {import('./util/selection-types.js').Scale} Scale
|
|
34
|
-
* @typedef {import('./util/selection-types.js').BinMethod} BinMethod
|
|
35
|
-
* @typedef {import('./util/selection-types.js').IntervalMetadata} IntervalMetadata
|
|
36
|
-
* @typedef {import('./util/selection-types.js').SelectionClause} SelectionClause
|
|
37
|
-
*/
|
|
26
|
+
export { queryFieldInfo } from './util/field-info.js';
|
|
27
|
+
export { jsType } from './util/js-type.js';
|
|
28
|
+
export { isActivatable } from './util/is-activatable.js';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { MosaicClient } from "./MosaicClient.js";
|
|
2
|
+
import {
|
|
3
|
+
coordinator as defaultCoordinator,
|
|
4
|
+
} from "./Coordinator.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} MakeClientOptions
|
|
8
|
+
* @property {import('./Coordinator.js').Coordinator} [coordinator] - Mosaic coordinator. Default to the global coordinator.
|
|
9
|
+
* @property {import('./Selection.js').Selection|null} [selection] - A selection whose predicates will be fed into the query function to produce the SQL query.
|
|
10
|
+
* @property {function(): Promise<void>} [prepare] - An async function to prepare the client before running queries.
|
|
11
|
+
* @property {function(any): any} query - A function that returns a query from a list of selection predicates.
|
|
12
|
+
* @property {function(any): void} [queryResult] - Called by the coordinator to return a query result.
|
|
13
|
+
* @property {function(): void} [queryPending] - Called by the coordinator to report a query execution error.
|
|
14
|
+
* @property {function(any): void} [queryError] - Called by the coordinator to inform the client that a query is pending.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Make a new client with the given options, and connect the client to the provided coordinator.
|
|
18
|
+
* @param {MakeClientOptions} options - The options for making the client
|
|
19
|
+
* @returns {MosaicClient & { destroy: () => void }} - The result object with methods to request an update or destroy the client.
|
|
20
|
+
*/
|
|
21
|
+
export function makeClient(options) {
|
|
22
|
+
const coordinator = options.coordinator ?? defaultCoordinator();
|
|
23
|
+
const client = new ProxyClient({ ...options, coordinator });
|
|
24
|
+
coordinator.connect(client);
|
|
25
|
+
return client;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** An internal class used to implement the makeClient API */
|
|
29
|
+
class ProxyClient extends MosaicClient {
|
|
30
|
+
/** @param {MakeClientOptions} options */
|
|
31
|
+
constructor(options) {
|
|
32
|
+
super(options.selection);
|
|
33
|
+
|
|
34
|
+
/** @type {MakeClientOptions} */
|
|
35
|
+
this._options = { ...options };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async prepare() {
|
|
39
|
+
await this._options.prepare?.();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
query(filter) {
|
|
43
|
+
return this._options.query(filter);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
queryResult(data) {
|
|
47
|
+
this._options.queryResult?.(data);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
queryPending() {
|
|
52
|
+
this._options.queryPending?.();
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
queryError(error) {
|
|
57
|
+
this._options.queryError?.(error);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
destroy() {
|
|
62
|
+
this._options.coordinator.disconnect(this);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AggregateNode, and, argmax, argmin, count, div, ExprNode, isNotNull, max, min, mul, pow, regrAvgX, regrAvgY, regrCount, sql, sqrt, sub, sum } from '@uwdata/mosaic-sql';
|
|
1
|
+
import { AggregateNode, and, argmax, argmin, count, div, exp, ExprNode, isNotNull, ln, max, min, mul, pow, regrAvgX, regrAvgY, regrCount, sql, sqrt, sub, sum } from '@uwdata/mosaic-sql';
|
|
2
2
|
import { fnv_hash } from '../util/hash.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -18,6 +18,8 @@ export function sufficientStatistics(node, preagg, avg) {
|
|
|
18
18
|
return sumExpr(preagg, node);
|
|
19
19
|
case 'avg':
|
|
20
20
|
return avgExpr(preagg, node);
|
|
21
|
+
case 'geomean':
|
|
22
|
+
return geomeanExpr(preagg, node);
|
|
21
23
|
case 'arg_max':
|
|
22
24
|
return argmaxExpr(preagg, node);
|
|
23
25
|
case 'arg_min':
|
|
@@ -155,6 +157,24 @@ function avgExpr(preagg, node) {
|
|
|
155
157
|
return div(sum(mul(as, name)), expr);
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Generate an expression for calculating geometric means over data dimensions.
|
|
162
|
+
* This method uses log-based computations to ensure numerical stability. The
|
|
163
|
+
* geomean calculation uses two sufficient statistics: the sum of log values
|
|
164
|
+
* and the count of non-null values. As a side effect, this method adds columns
|
|
165
|
+
* for these statistics to the input *preagg* object.
|
|
166
|
+
* @param {Record<string, ExprNode>} preagg A map of columns (such as
|
|
167
|
+
* sufficient statistics) to pre-aggregate.
|
|
168
|
+
* @param {AggregateNode} node The originating aggregate function call.
|
|
169
|
+
* @returns {ExprNode} An aggregate expression over pre-aggregated dimensions.
|
|
170
|
+
*/
|
|
171
|
+
function geomeanExpr(preagg, node) {
|
|
172
|
+
const x = node.args[0];
|
|
173
|
+
const expr = addStat(preagg, sum(ln(x)), node);
|
|
174
|
+
const { expr: n } = countExpr(preagg, node);
|
|
175
|
+
return exp(div(sum(expr), n));
|
|
176
|
+
}
|
|
177
|
+
|
|
158
178
|
/**
|
|
159
179
|
* Generate an expression for calculating argmax over data dimensions.
|
|
160
180
|
* As a side effect, this method adds a column to the input *preagg* object
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { DescribeQuery, ExprNode, Query } from '@uwdata/mosaic-sql';
|
|
2
|
+
|
|
3
|
+
/** Query type accepted by a coordinator. */
|
|
4
|
+
export type QueryType =
|
|
5
|
+
| string
|
|
6
|
+
| Query
|
|
7
|
+
| DescribeQuery;
|
|
8
|
+
|
|
9
|
+
/** String indicating a JavaScript data type. */
|
|
10
|
+
export type JSType =
|
|
11
|
+
| 'number'
|
|
12
|
+
| 'date'
|
|
13
|
+
| 'boolean'
|
|
14
|
+
| 'string'
|
|
15
|
+
| 'array'
|
|
16
|
+
| 'object';
|
|
17
|
+
|
|
18
|
+
/** String indicating a requested summary statistic. */
|
|
19
|
+
export type Stat =
|
|
20
|
+
| 'count'
|
|
21
|
+
| 'nulls'
|
|
22
|
+
| 'max'
|
|
23
|
+
| 'min'
|
|
24
|
+
| 'distinct';
|
|
25
|
+
|
|
26
|
+
/** A reference to a database column or expression. */
|
|
27
|
+
export type FieldRef = string | ExprNode;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A request for metadata information about a database column.
|
|
31
|
+
*/
|
|
32
|
+
export interface FieldInfoRequest {
|
|
33
|
+
table: string;
|
|
34
|
+
column: FieldRef;
|
|
35
|
+
stats?: Stat[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A response with metadata information about a database column.
|
|
40
|
+
*/
|
|
41
|
+
export interface FieldInfo extends Partial<Record<Stat, number>> {
|
|
42
|
+
table: string,
|
|
43
|
+
column: string,
|
|
44
|
+
sqlType: string,
|
|
45
|
+
type: JSType,
|
|
46
|
+
nullable: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A result row from a DESCRIBE query. */
|
|
50
|
+
export interface ColumnDescription {
|
|
51
|
+
column_name: string,
|
|
52
|
+
column_type: string,
|
|
53
|
+
null: 'YES' | 'NO'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Interface for components that perform selection activation.
|
|
58
|
+
*/
|
|
59
|
+
export interface Activatable {
|
|
60
|
+
/**
|
|
61
|
+
* Activate the selection that this component publishes to.
|
|
62
|
+
*/
|
|
63
|
+
activate(): void;
|
|
64
|
+
}
|
package/src/util/field-info.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AggregateNode, Query, asTableRef, count, isNull, max, min, sql } from '@uwdata/mosaic-sql';
|
|
1
|
+
import { AggregateNode, Query, asTableRef, count, isAggregateExpression, isNode, isNull, max, min, sql } from '@uwdata/mosaic-sql';
|
|
2
2
|
import { jsType } from './js-type.js';
|
|
3
3
|
|
|
4
4
|
export const Count = 'count';
|
|
@@ -9,7 +9,10 @@ export const Distinct = 'distinct';
|
|
|
9
9
|
export const Stats = { Count, Nulls, Max, Min, Distinct };
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @type {Record<
|
|
12
|
+
* @type {Record<
|
|
13
|
+
* import('../types.js').Stat,
|
|
14
|
+
* (column: import('../types.js').FieldRef) => AggregateNode
|
|
15
|
+
* >}
|
|
13
16
|
*/
|
|
14
17
|
const statMap = {
|
|
15
18
|
[Count]: count,
|
|
@@ -20,18 +23,26 @@ const statMap = {
|
|
|
20
23
|
};
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
|
-
*
|
|
24
|
-
* @param {
|
|
25
|
-
* @
|
|
26
|
-
* @param {string[]|Set<string>} stats
|
|
27
|
-
* @returns
|
|
26
|
+
* Get summary stats of the given column
|
|
27
|
+
* @param {import('../types.js').FieldInfoRequest} field
|
|
28
|
+
* @returns {Query}
|
|
28
29
|
*/
|
|
29
|
-
function summarize(table, column, stats) {
|
|
30
|
+
function summarize({ table, column, stats }) {
|
|
30
31
|
return Query
|
|
31
32
|
.from(table)
|
|
32
|
-
.select(Array.from(stats, s => ({[s]: statMap[s](column)})));
|
|
33
|
+
.select(Array.from(stats, s => ({ [s]: statMap[s](column) })));
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Queries information about fields of a table.
|
|
38
|
+
* If the `fields` array contains a single field with the column set to '*',
|
|
39
|
+
* the function will retrieve and return the table information using `getTableInfo`.
|
|
40
|
+
* Otherwise, it will query individual field information using `getFieldInfo`
|
|
41
|
+
* for each field in the `fields` array.
|
|
42
|
+
* @param {import('../Coordinator.js').Coordinator} mc A Mosaic coordinator.
|
|
43
|
+
* @param {import('../types.js').FieldInfoRequest[]} fields
|
|
44
|
+
* @returns {Promise<import('../types.js').FieldInfo[]>}
|
|
45
|
+
*/
|
|
35
46
|
export async function queryFieldInfo(mc, fields) {
|
|
36
47
|
if (fields.length === 1 && fields[0].column === '*') {
|
|
37
48
|
return getTableInfo(mc, fields[0].table);
|
|
@@ -42,13 +53,21 @@ export async function queryFieldInfo(mc, fields) {
|
|
|
42
53
|
}
|
|
43
54
|
}
|
|
44
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Get information about a single field of a table.
|
|
58
|
+
* @param {import('../Coordinator.js').Coordinator} mc A Mosaic coordinator.
|
|
59
|
+
* @param {import('../types.js').FieldInfoRequest} field
|
|
60
|
+
* @returns {Promise<import('../types.js').FieldInfo>}
|
|
61
|
+
*/
|
|
45
62
|
async function getFieldInfo(mc, { table, column, stats }) {
|
|
46
63
|
// generate and issue a query for field metadata info
|
|
47
64
|
// use GROUP BY ALL to differentiate & consolidate aggregates
|
|
48
65
|
const q = Query
|
|
49
66
|
.from({ source: table })
|
|
50
67
|
.select({ column })
|
|
51
|
-
.groupby(column
|
|
68
|
+
.groupby(isNode(column) && isAggregateExpression(column) ? sql`ALL` : []);
|
|
69
|
+
|
|
70
|
+
/** @type {import('../types.js').ColumnDescription[]} */
|
|
52
71
|
const [desc] = Array.from(await mc.query(Query.describe(q)));
|
|
53
72
|
const info = {
|
|
54
73
|
table,
|
|
@@ -59,11 +78,11 @@ async function getFieldInfo(mc, { table, column, stats }) {
|
|
|
59
78
|
};
|
|
60
79
|
|
|
61
80
|
// no need for summary statistics
|
|
62
|
-
if (!
|
|
81
|
+
if (!stats?.length) return info;
|
|
63
82
|
|
|
64
83
|
// query for summary stats
|
|
65
84
|
const [result] = await mc.query(
|
|
66
|
-
summarize(table, column, stats),
|
|
85
|
+
summarize({ table, column, stats }),
|
|
67
86
|
{ persist: true }
|
|
68
87
|
);
|
|
69
88
|
|
|
@@ -71,9 +90,16 @@ async function getFieldInfo(mc, { table, column, stats }) {
|
|
|
71
90
|
return Object.assign(info, result);
|
|
72
91
|
}
|
|
73
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Get information about the fields of a table.
|
|
95
|
+
* @param {import('../Coordinator.js').Coordinator} mc A Mosaic coordinator.
|
|
96
|
+
* @param {string} table The table name.
|
|
97
|
+
* @returns {Promise<import('../types.js').FieldInfo[]>}
|
|
98
|
+
*/
|
|
74
99
|
async function getTableInfo(mc, table) {
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
/** @type {import('../types.js').ColumnDescription[]} */
|
|
101
|
+
const result = Array.from(await mc.query(`DESCRIBE ${asTableRef(table)}`));
|
|
102
|
+
return result.map(desc => ({
|
|
77
103
|
table,
|
|
78
104
|
column: desc.column_name,
|
|
79
105
|
sqlType: desc.column_type,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test if a value implements the Activatable interface.
|
|
3
|
+
* @param {*} value The value to test.
|
|
4
|
+
* @returns {value is import('../types.js').Activatable}
|
|
5
|
+
*/
|
|
6
|
+
export function isActivatable(value) {
|
|
7
|
+
return typeof value?.activate === 'function' && value.activate.length === 0;
|
|
8
|
+
}
|
package/src/util/js-type.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps a SQL data type to its corresponding JavaScript type.
|
|
3
|
+
* @param {string} type The name of a SQL data type
|
|
4
|
+
* @returns {import('../types.js').JSType} The corresponding JavaScript type name
|
|
5
|
+
* @throws {Error} Throws an error if the given SQL type name is unsupported or unrecognized.
|
|
6
|
+
*/
|
|
1
7
|
export function jsType(type) {
|
|
2
8
|
switch (type) {
|
|
3
9
|
case 'BIGINT':
|
package/src/util/throttle.js
CHANGED
|
@@ -5,10 +5,12 @@ const NIL = {};
|
|
|
5
5
|
* a Promise. Upon repeated invocation, the callback will not be invoked
|
|
6
6
|
* until a prior Promise resolves. If multiple invocations occurs while
|
|
7
7
|
* waiting, only the most recent invocation will be pending.
|
|
8
|
-
* @
|
|
8
|
+
* @template E, T
|
|
9
|
+
* @param {(event: E) => Promise<T>} callback The callback function.
|
|
9
10
|
* @param {boolean} [debounce=true] Flag indicating if invocations
|
|
10
11
|
* should also be debounced within the current animation frame.
|
|
11
|
-
* @returns A new function that throttles
|
|
12
|
+
* @returns {(event: E) => void} A new function that throttles
|
|
13
|
+
* access to the callback.
|
|
12
14
|
*/
|
|
13
15
|
export function throttle(callback, debounce = false) {
|
|
14
16
|
let curr;
|
package/vitest.config.ts
ADDED