@uwdata/mosaic-core 0.14.0 → 0.15.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.
@@ -14,8 +14,7 @@ export function coordinator(instance?: Coordinator): Coordinator;
14
14
  * @param {*} [options.manager] The query manager to use.
15
15
  * @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching.
16
16
  * @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation.
17
- * @param {import('./preagg/PreAggregator.js').PreAggregateOptions} [options.preagg]
18
- * Options for the Pre-aggregator.
17
+ * @param {PreAggregateOptions} [options.preagg] Options for the Pre-aggregator.
19
18
  */
20
19
  export class Coordinator {
21
20
  constructor(db?: {
@@ -70,22 +69,21 @@ export class Coordinator {
70
69
  cancel(requests: QueryResult[]): void;
71
70
  /**
72
71
  * Issue a query for which no result (return value) is needed.
73
- * @param { import('./types.js').QueryType[] |
74
- * import('./types.js').QueryType} query The query or an array of queries.
72
+ * @param {QueryType[] | QueryType} query The query or an array of queries.
75
73
  * Each query should be either a Query builder object or a SQL string.
76
74
  * @param {object} [options] An options object.
77
75
  * @param {number} [options.priority] The query priority, defaults to
78
76
  * `Priority.Normal`.
79
77
  * @returns {QueryResult} A query result promise.
80
78
  */
81
- exec(query: import("./types.js").QueryType[] | import("./types.js").QueryType, { priority }?: {
79
+ exec(query: QueryType[] | QueryType, { priority }?: {
82
80
  priority?: number;
83
81
  }): QueryResult;
84
82
  /**
85
83
  * Issue a query to the backing database. The submitted query may be
86
84
  * consolidate with other queries and its results may be cached.
87
- * @param {import('./types.js').QueryType} query The query as either a Query
88
- * builder object or a SQL string.
85
+ * @param {QueryType} query The query as either a Query builder objec
86
+ * or a SQL string.
89
87
  * @param {object} [options] An options object.
90
88
  * @param {'arrow' | 'json'} [options.type] The query result format type.
91
89
  * @param {boolean} [options.cache=true] If true, cache the query result
@@ -96,7 +94,7 @@ export class Coordinator {
96
94
  * `Priority.Normal`.
97
95
  * @returns {QueryResult} A query result promise.
98
96
  */
99
- query(query: import("./types.js").QueryType, { type, cache, priority, ...options }?: {
97
+ query(query: QueryType, { type, cache, priority, ...options }?: {
100
98
  type?: "arrow" | "json";
101
99
  cache?: boolean;
102
100
  persist?: boolean;
@@ -105,13 +103,13 @@ export class Coordinator {
105
103
  /**
106
104
  * Issue a query to prefetch data for later use. The query result is cached
107
105
  * for efficient future access.
108
- * @param {import('./types.js').QueryType} query The query as either a Query
109
- * builder object or a SQL string.
106
+ * @param {QueryType} query The query as either a Query builder object
107
+ * or a SQL string.
110
108
  * @param {object} [options] An options object.
111
109
  * @param {'arrow' | 'json'} [options.type] The query result format type.
112
110
  * @returns {QueryResult} A query result promise.
113
111
  */
114
- prefetch(query: import("./types.js").QueryType, options?: {
112
+ prefetch(query: QueryType, options?: {
115
113
  type?: "arrow" | "json";
116
114
  }): QueryResult;
117
115
  /**
@@ -138,25 +136,24 @@ export class Coordinator {
138
136
  * Update client data by submitting the given query and returning the
139
137
  * data (or error) to the client.
140
138
  * @param {MosaicClient} client A Mosaic client.
141
- * @param {import('./types.js').QueryType} query The data query.
139
+ * @param {QueryType} query The data query.
142
140
  * @param {number} [priority] The query priority.
143
141
  * @returns {Promise} A Promise that resolves upon completion of the update.
144
142
  */
145
- updateClient(client: MosaicClient, query: import("./types.js").QueryType, priority?: number): Promise<any>;
143
+ updateClient(client: MosaicClient, query: QueryType, priority?: number): Promise<any>;
146
144
  /**
147
145
  * Issue a query request for a client. If the query is null or undefined,
148
146
  * the client is simply updated. Otherwise `updateClient` is called. As a
149
147
  * side effect, this method clears the current preaggregator state.
150
148
  * @param {MosaicClient} client The client to update.
151
- * @param {import('./types.js').QueryType | null} [query] The query to issue.
149
+ * @param {QueryType | null} [query] The query to issue.
152
150
  */
153
- requestQuery(client: MosaicClient, query?: import("./types.js").QueryType | null): Promise<any>;
151
+ requestQuery(client: MosaicClient, query?: QueryType | null): Promise<any>;
154
152
  /**
155
153
  * Connect a client to the coordinator.
156
154
  * @param {MosaicClient} client The Mosaic client to connect.
157
155
  */
158
156
  connect(client: MosaicClient): void;
159
- initializeClient(client: any): Promise<any>;
160
157
  /**
161
158
  * Disconnect a client from the coordinator.
162
159
  * @param {MosaicClient} client The Mosaic client to disconnect.
@@ -165,5 +162,6 @@ export class Coordinator {
165
162
  }
166
163
  import { QueryManager } from './QueryManager.js';
167
164
  import { PreAggregator } from './preagg/PreAggregator.js';
168
- import { QueryResult } from './util/query-result.js';
169
- import { MosaicClient } from './MosaicClient.js';
165
+ import type { QueryResult } from './util/query-result.js';
166
+ import type { QueryType } from './types.js';
167
+ import type { MosaicClient } from './MosaicClient.js';
@@ -1,14 +1,27 @@
1
1
  /**
2
- * Base class for Mosaic clients.
2
+ * A Mosaic client is a data consumer that indicates its data needs to a
3
+ * Mosaic coordinator via the query method. The coordinator is responsible
4
+ * for issuing queries and returning results to the client.
5
+ *
6
+ * The client life-cycle consists of connection to a coordinator,
7
+ * initialization (potentially involving queries for data schema and summary
8
+ * statistic information), and then interactive queries that may be driven by
9
+ * an associated selection. When no longer needed, a client should be
10
+ * disconnected from the coordinator.
11
+ *
12
+ * When enabled, a client will initialize and respond to query update requests.
13
+ * If disabled, the client will delay initialization and not respond to queries
14
+ * until enabled again. Disabling a client can improve system performance when
15
+ * associated interface elements are offscreen or disabled.
3
16
  */
4
17
  export class MosaicClient {
5
18
  /**
6
- * Constructor.
7
- * @param {*} filterSelection An optional selection to interactively filter
8
- * this client's data. If provided, a coordinator will re-query and update
9
- * the client when the selection updates.
19
+ * Create a new client instance.
20
+ * @param {Selection} [filterSelection] An optional selection to
21
+ * interactively filter this client's data. If provided, a coordinator
22
+ * will re-query and update the client when the selection updates.
10
23
  */
11
- constructor(filterSelection: any);
24
+ constructor(filterSelection?: Selection);
12
25
  /** @type {Selection} */
13
26
  _filterBy: Selection;
14
27
  _requestUpdate: (event: any) => void;
@@ -16,6 +29,12 @@ export class MosaicClient {
16
29
  _coordinator: Coordinator;
17
30
  /** @type {Promise<any>} */
18
31
  _pending: Promise<any>;
32
+ /** @type {boolean} */
33
+ _enabled: boolean;
34
+ /** @type {boolean} */
35
+ _initialized: boolean;
36
+ /** @type {Query | boolean} */
37
+ _request: Query | boolean;
19
38
  /**
20
39
  * Set this client's connected coordinator.
21
40
  */
@@ -24,6 +43,14 @@ export class MosaicClient {
24
43
  * Return this client's connected coordinator.
25
44
  */
26
45
  get coordinator(): Coordinator;
46
+ /**
47
+ * Set this client's enabled state;
48
+ */
49
+ set enabled(state: boolean);
50
+ /**
51
+ * Return this client's enabled state.
52
+ */
53
+ get enabled(): boolean;
27
54
  /**
28
55
  * Return a Promise that resolves once the client has updated.
29
56
  */
@@ -34,25 +61,15 @@ export class MosaicClient {
34
61
  get filterBy(): Selection;
35
62
  /**
36
63
  * Return a boolean indicating if the client query can be sped up with
37
- * materialized views of pre-aggregated data. Should return true if changes to
38
- * the filterBy selection does not change the groupby domain of the client
64
+ * materialized views of pre-aggregated data. Should return true if changes
65
+ * to the filterBy selection do not change the groupby domain of the client
39
66
  * query.
40
67
  */
41
68
  get filterStable(): boolean;
42
69
  /**
43
- * Return an array of fields queried by this client.
44
- * @returns {import('./types.js').FieldInfoRequest[] | null}
45
- * The fields to retrieve info for.
46
- */
47
- fields(): import("./types.js").FieldInfoRequest[] | null;
48
- /**
49
- * Called by the coordinator to set the field info for this client.
50
- * @param {import('./types.js').FieldInfo[]} info The field info result.
51
- * @returns {this}
52
- */
53
- fieldInfo(info: import("./types.js").FieldInfo[]): this;
54
- /**
55
- * Prepare the client before the query() method is called.
70
+ * Prepare the client before the `query()` method is called. Subclasses
71
+ * should override this method as needed, potentially issuing one or more
72
+ * queries to gather data or metadata needed prior to `query` calls.
56
73
  */
57
74
  prepare(): Promise<void>;
58
75
  /**
@@ -80,26 +97,31 @@ export class MosaicClient {
80
97
  queryError(error: any): this;
81
98
  /**
82
99
  * Request the coordinator to execute a query for this client.
83
- * If an explicit query is not provided, the client query method will
84
- * be called, filtered by the current filterBy selection. This method
85
- * has no effect if the client is not registered with a coordinator.
100
+ * If an explicit query is not provided, the client `query` method will
101
+ * be called, filtered by the current `filterBy` selection. This method has
102
+ * no effect if the client is not connected to a coordinator. If the client
103
+ * is connected by currently disabled, the request will be serviced if the
104
+ * client is later enabled.
105
+ * @param {Query} [query] The query to request. If unspecified, the query
106
+ * will be determind by the client's `query` method and the current
107
+ * `filterBy` selection state.
86
108
  * @returns {Promise}
87
109
  */
88
- requestQuery(query: any): Promise<any>;
110
+ requestQuery(query?: Query): Promise<any>;
89
111
  /**
90
112
  * Request that the coordinator perform a throttled update of this client
91
- * using the default query. Unlike requestQuery, for which every call will
92
- * result in an executed query, multiple calls to requestUpdate may be
93
- * consolidated into a single update.
113
+ * using the default query. Unlike requestQuery, for which every call results
114
+ * in an executed query, multiple calls to requestUpdate may be consolidated
115
+ * into a single update. This method has no effect if the client is not
116
+ * connected to a coordinator. If the client is connected but currently
117
+ * disabled, the request will be serviced if the client is later enabled.
94
118
  */
95
119
  requestUpdate(): void;
96
120
  /**
97
- * Reset this client, initiating new field info, call the prepare method,
98
- * and query requests. This method has no effect if the client is not
99
- * registered with a coordinator.
100
- * @returns {Promise}
121
+ * Reset this client, calling the prepare method and query requests. This
122
+ * method has no effect if the client is not registered with a coordinator.
101
123
  */
102
- initialize(): Promise<any>;
124
+ initialize(): void;
103
125
  /**
104
126
  * Requests a client update, for example to (re-)render an interface
105
127
  * component.
@@ -107,5 +129,6 @@ export class MosaicClient {
107
129
  */
108
130
  update(): this | Promise<any>;
109
131
  }
110
- import { Selection } from './Selection.js';
111
- import { Coordinator } from './Coordinator.js';
132
+ import type { Selection } from './Selection.js';
133
+ import type { Coordinator } from './Coordinator.js';
134
+ import type { Query } from '@uwdata/mosaic-sql';
@@ -1,48 +1,78 @@
1
1
  /**
2
2
  * @typedef {Object} MakeClientOptions
3
- * @property {import('./Coordinator.js').Coordinator} [coordinator] - Mosaic coordinator. Default to the global coordinator.
4
- * @property {import('./Selection.js').Selection|null} [selection] - A selection whose predicates will be fed into the query function to produce the SQL query.
5
- * @property {function(): Promise<void>} [prepare] - An async function to prepare the client before running queries.
6
- * @property {function(any): any} query - A function that returns a query from a list of selection predicates.
7
- * @property {function(any): void} [queryResult] - Called by the coordinator to return a query result.
8
- * @property {function(): void} [queryPending] - Called by the coordinator to report a query execution error.
9
- * @property {function(any): void} [queryError] - Called by the coordinator to inform the client that a query is pending.
3
+ * @property {Coordinator} [coordinator] Mosaic coordinator.
4
+ * Defaults to the global coordinator.
5
+ * @property {Selection|null} [selection] A selection whose predicates are
6
+ * fed into the query function to produce the SQL query.
7
+ * @property {boolean} [enabled] A flag (default `true`) indicating if the
8
+ * client should initially be enabled or not.
9
+ * @property {boolean} [filterStable] A flag (default `true`) indicating if the
10
+ * if client queries can be sped up using pre-aggregated data. Should be set
11
+ * to `false` if filtering changes the groupby domain of the query.
12
+ * @property {function(): Promise<void>} [prepare]
13
+ * An async function to prepare the client before running queries.
14
+ * @property {function(any): any} [query]
15
+ * A function that returns a query from a list of selection predicates.
16
+ * @property {function(any): void} [queryResult]
17
+ * Called by the coordinator to return a query result.
18
+ * @property {function(): void} [queryPending]
19
+ * Called by the coordinator to report a query execution error.
20
+ * @property {function(any): void} [queryError]
21
+ * Called by the coordinator to inform the client that a query is pending.
10
22
  */
11
- /** Make a new client with the given options, and connect the client to the provided coordinator.
12
- * @param {MakeClientOptions} options - The options for making the client
13
- * @returns {MosaicClient & { destroy: () => void }} - The result object with methods to request an update or destroy the client.
23
+ /**
24
+ * Make a new client with the given options, and connect the client to the
25
+ * provided coordinator.
26
+ * @param {MakeClientOptions} options The options for making the client.
27
+ * @returns {MosaicClient & { destroy: () => void }} The resulting client,
28
+ * along with a method to destroy the client when no longer needed.
14
29
  */
15
30
  export function makeClient(options: MakeClientOptions): MosaicClient & {
16
31
  destroy: () => void;
17
32
  };
18
33
  export type MakeClientOptions = {
19
34
  /**
20
- * - Mosaic coordinator. Default to the global coordinator.
35
+ * Mosaic coordinator.
36
+ * Defaults to the global coordinator.
37
+ */
38
+ coordinator?: Coordinator;
39
+ /**
40
+ * A selection whose predicates are
41
+ * fed into the query function to produce the SQL query.
42
+ */
43
+ selection?: Selection | null;
44
+ /**
45
+ * A flag (default `true`) indicating if the
46
+ * client should initially be enabled or not.
21
47
  */
22
- coordinator?: import("./Coordinator.js").Coordinator;
48
+ enabled?: boolean;
23
49
  /**
24
- * - A selection whose predicates will be fed into the query function to produce the SQL query.
50
+ * A flag (default `true`) indicating if the
51
+ * if client queries can be sped up using pre-aggregated data. Should be set
52
+ * to `false` if filtering changes the groupby domain of the query.
25
53
  */
26
- selection?: import("./Selection.js").Selection | null;
54
+ filterStable?: boolean;
27
55
  /**
28
- * - An async function to prepare the client before running queries.
56
+ * An async function to prepare the client before running queries.
29
57
  */
30
58
  prepare?: () => Promise<void>;
31
59
  /**
32
- * - A function that returns a query from a list of selection predicates.
60
+ * A function that returns a query from a list of selection predicates.
33
61
  */
34
- query: (arg0: any) => any;
62
+ query?: (arg0: any) => any;
35
63
  /**
36
- * - Called by the coordinator to return a query result.
64
+ * Called by the coordinator to return a query result.
37
65
  */
38
66
  queryResult?: (arg0: any) => void;
39
67
  /**
40
- * - Called by the coordinator to report a query execution error.
68
+ * Called by the coordinator to report a query execution error.
41
69
  */
42
70
  queryPending?: () => void;
43
71
  /**
44
- * - Called by the coordinator to inform the client that a query is pending.
72
+ * Called by the coordinator to inform the client that a query is pending.
45
73
  */
46
74
  queryError?: (arg0: any) => void;
47
75
  };
48
- import { MosaicClient } from "./MosaicClient.js";
76
+ import { MosaicClient } from './MosaicClient.js';
77
+ import type { Coordinator } from './Coordinator.js';
78
+ import type { Selection } from './Selection.js';
@@ -27,17 +27,17 @@
27
27
  export class PreAggregator {
28
28
  /**
29
29
  * Create a new manager of materialized views of pre-aggregated data.
30
- * @param {import('../Coordinator.js').Coordinator} coordinator A Mosaic coordinator.
30
+ * @param {Coordinator} coordinator A Mosaic coordinator.
31
31
  * @param {PreAggregateOptions} [options] Pre-aggregation options.
32
32
  */
33
- constructor(coordinator: import("../Coordinator.js").Coordinator, { schema, enabled }?: PreAggregateOptions);
34
- /** @type {Map<import('../MosaicClient.js').MosaicClient, PreAggregateInfo | Skip | null>} */
35
- entries: Map<import("../MosaicClient.js").MosaicClient, PreAggregateInfo | {
33
+ constructor(coordinator: Coordinator, { schema, enabled }?: PreAggregateOptions);
34
+ /** @type {Map<MosaicClient, PreAggregateInfo | Skip | null>} */
35
+ entries: Map<MosaicClient, PreAggregateInfo | {
36
36
  skip: boolean;
37
37
  result: any;
38
38
  } | null>;
39
39
  active: any;
40
- mc: import("../Coordinator.js").Coordinator;
40
+ mc: Coordinator;
41
41
  _schema: string;
42
42
  _enabled: boolean;
43
43
  /**
@@ -88,16 +88,14 @@ export class PreAggregator {
88
88
  * client-selection pair, or null if the client has unstable filters.
89
89
  * This method has multiple possible side effects, including materialized
90
90
  * view creation and updating internal caches.
91
- * @param {import('../MosaicClient.js').MosaicClient} client A Mosaic client.
92
- * @param {import('../Selection.js').Selection} selection A Mosaic selection
93
- * to filter the client by.
94
- * @param {import('../util/selection-types.js').SelectionClause} activeClause
95
- * A representative active selection clause for which to (possibly) generate
96
- * materialized views of pre-aggregates.
91
+ * @param {MosaicClient} client A Mosaic client.
92
+ * @param {Selection} selection A Mosaic selection to filter the client by.
93
+ * @param {SelectionClause} activeClause A representative active selection
94
+ * clause for which to generate materialized views of pre-aggregates.
97
95
  * @returns {PreAggregateInfo | Skip | null} Information and query generator
98
96
  * for pre-aggregated tables, or null if the client has unstable filters.
99
97
  */
100
- request(client: import("../MosaicClient.js").MosaicClient, selection: import("../Selection.js").Selection, activeClause: import("../util/selection-types.js").SelectionClause): PreAggregateInfo | {
98
+ request(client: MosaicClient, selection: Selection, activeClause: SelectionClause): PreAggregateInfo | {
101
99
  skip: boolean;
102
100
  result: any;
103
101
  } | null;
@@ -157,11 +155,10 @@ export class PreAggregateInfo {
157
155
  skip: boolean;
158
156
  /**
159
157
  * Generate a materialized view query for the given predicate.
160
- * @param {import('@uwdata/mosaic-sql').ExprNode} predicate The current
161
- * active clause predicate.
158
+ * @param {ExprNode} predicate The current active clause predicate.
162
159
  * @returns {SelectQuery} A materialized view query.
163
160
  */
164
- query(predicate: import("@uwdata/mosaic-sql").ExprNode): SelectQuery;
161
+ query(predicate: ExprNode): SelectQuery;
165
162
  }
166
163
  export type PreAggregateOptions = {
167
164
  /**
@@ -175,4 +172,9 @@ export type PreAggregateOptions = {
175
172
  */
176
173
  enabled?: boolean;
177
174
  };
175
+ import type { MosaicClient } from '../MosaicClient.js';
176
+ import type { Coordinator } from '../Coordinator.js';
177
+ import type { Selection } from '../Selection.js';
178
+ import type { SelectionClause } from '../util/selection-types.js';
178
179
  import { SelectQuery } from '@uwdata/mosaic-sql';
180
+ import type { ExprNode } from '@uwdata/mosaic-sql';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uwdata/mosaic-core",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Scalable and extensible linked data views.",
5
5
  "keywords": [
6
6
  "mosaic",
@@ -32,10 +32,10 @@
32
32
  "dependencies": {
33
33
  "@duckdb/duckdb-wasm": "^1.29.0",
34
34
  "@uwdata/flechette": "^2.0.0",
35
- "@uwdata/mosaic-sql": "^0.14.0"
35
+ "@uwdata/mosaic-sql": "^0.15.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@uwdata/mosaic-duckdb": "^0.14.0"
38
+ "@uwdata/mosaic-duckdb": "^0.15.0"
39
39
  },
40
- "gitHead": "a882aab60867e4e9d9738bc950aa9de32729a806"
40
+ "gitHead": "671ad1ba86749a8435bd4aa7e722e2a8553f2cb0"
41
41
  }
@@ -1,9 +1,12 @@
1
+ /** @import { PreAggregateOptions } from './preagg/PreAggregator.js' */
2
+ /** @import { QueryResult } from './util/query-result.js' */
3
+ /** @import { SelectionClause } from './util/selection-types.js' */
4
+ /** @import { MosaicClient } from './MosaicClient.js' */
5
+ /** @import { Selection } from './Selection.js' */
6
+ /** @import { QueryType } from './types.js' */
1
7
  import { socketConnector } from './connectors/socket.js';
2
8
  import { PreAggregator } from './preagg/PreAggregator.js';
3
- import { queryFieldInfo } from './util/field-info.js';
4
- import { QueryResult } from './util/query-result.js';
5
9
  import { voidLogger } from './util/void-logger.js';
6
- import { MosaicClient } from './MosaicClient.js';
7
10
  import { QueryManager, Priority } from './QueryManager.js';
8
11
 
9
12
  /**
@@ -36,8 +39,7 @@ export function coordinator(instance) {
36
39
  * @param {*} [options.manager] The query manager to use.
37
40
  * @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching.
38
41
  * @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation.
39
- * @param {import('./preagg/PreAggregator.js').PreAggregateOptions} [options.preagg]
40
- * Options for the Pre-aggregator.
42
+ * @param {PreAggregateOptions} [options.preagg] Options for the Pre-aggregator.
41
43
  */
42
44
  export class Coordinator {
43
45
  constructor(db = socketConnector(), {
@@ -110,8 +112,7 @@ export class Coordinator {
110
112
 
111
113
  /**
112
114
  * Issue a query for which no result (return value) is needed.
113
- * @param { import('./types.js').QueryType[] |
114
- * import('./types.js').QueryType} query The query or an array of queries.
115
+ * @param {QueryType[] | QueryType} query The query or an array of queries.
115
116
  * Each query should be either a Query builder object or a SQL string.
116
117
  * @param {object} [options] An options object.
117
118
  * @param {number} [options.priority] The query priority, defaults to
@@ -126,8 +127,8 @@ export class Coordinator {
126
127
  /**
127
128
  * Issue a query to the backing database. The submitted query may be
128
129
  * consolidate with other queries and its results may be cached.
129
- * @param {import('./types.js').QueryType} query The query as either a Query
130
- * builder object or a SQL string.
130
+ * @param {QueryType} query The query as either a Query builder objec
131
+ * or a SQL string.
131
132
  * @param {object} [options] An options object.
132
133
  * @param {'arrow' | 'json'} [options.type] The query result format type.
133
134
  * @param {boolean} [options.cache=true] If true, cache the query result
@@ -150,8 +151,8 @@ export class Coordinator {
150
151
  /**
151
152
  * Issue a query to prefetch data for later use. The query result is cached
152
153
  * for efficient future access.
153
- * @param {import('./types.js').QueryType} query The query as either a Query
154
- * builder object or a SQL string.
154
+ * @param {QueryType} query The query as either a Query builder object
155
+ * or a SQL string.
155
156
  * @param {object} [options] An options object.
156
157
  * @param {'arrow' | 'json'} [options.type] The query result format type.
157
158
  * @returns {QueryResult} A query result promise.
@@ -190,7 +191,7 @@ export class Coordinator {
190
191
  * Update client data by submitting the given query and returning the
191
192
  * data (or error) to the client.
192
193
  * @param {MosaicClient} client A Mosaic client.
193
- * @param {import('./types.js').QueryType} query The data query.
194
+ * @param {QueryType} query The data query.
194
195
  * @param {number} [priority] The query priority.
195
196
  * @returns {Promise} A Promise that resolves upon completion of the update.
196
197
  */
@@ -209,7 +210,7 @@ export class Coordinator {
209
210
  * the client is simply updated. Otherwise `updateClient` is called. As a
210
211
  * side effect, this method clears the current preaggregator state.
211
212
  * @param {MosaicClient} client The client to update.
212
- * @param {import('./types.js').QueryType | null} [query] The query to issue.
213
+ * @param {QueryType | null} [query] The query to issue.
213
214
  */
214
215
  requestQuery(client, query) {
215
216
  this.preaggregator.clear();
@@ -236,26 +237,12 @@ export class Coordinator {
236
237
  client.coordinator = this;
237
238
 
238
239
  // initialize client lifecycle
239
- client._pending = this.initializeClient(client);
240
+ client.initialize();
240
241
 
241
242
  // connect filter selection
242
243
  connectSelection(this, client.filterBy, client);
243
244
  }
244
245
 
245
- async initializeClient(client) {
246
- // retrieve field statistics
247
- const fields = client.fields();
248
- if (fields?.length) {
249
- client.fieldInfo(await queryFieldInfo(this, fields));
250
- }
251
-
252
- // prepare the client
253
- await client.prepare();
254
-
255
- // request data query
256
- return client.requestQuery();
257
- }
258
-
259
246
  /**
260
247
  * Disconnect a client from the coordinator.
261
248
  * @param {MosaicClient} client The Mosaic client to disconnect.
@@ -276,7 +263,7 @@ export class Coordinator {
276
263
  /**
277
264
  * Connect a selection-client pair to the coordinator to process updates.
278
265
  * @param {Coordinator} mc The Mosaic coordinator.
279
- * @param {import('./Selection.js').Selection} selection A selection.
266
+ * @param {Selection} selection A selection.
280
267
  * @param {MosaicClient} client A Mosiac client that is filtered by the
281
268
  * given selection.
282
269
  */
@@ -308,15 +295,16 @@ function connectSelection(mc, selection, client) {
308
295
  * next updates. Activation provides a preview of likely next events,
309
296
  * enabling potential precomputation to optimize updates.
310
297
  * @param {Coordinator} mc The Mosaic coordinator.
311
- * @param {import('./Selection.js').Selection} selection A selection.
312
- * @param {import('./util/selection-types.js').SelectionClause} clause A
313
- * selection clause representative of the activation.
298
+ * @param {Selection} selection A selection.
299
+ * @param {SelectionClause} clause A selection clause for the activation.
314
300
  */
315
301
  function activateSelection(mc, selection, clause) {
316
302
  const { preaggregator, filterGroups } = mc;
317
303
  const { clients } = filterGroups.get(selection);
318
304
  for (const client of clients) {
319
- preaggregator.request(client, selection, clause);
305
+ if (client.enabled) {
306
+ preaggregator.request(client, selection, clause);
307
+ }
320
308
  }
321
309
  }
322
310
 
@@ -324,7 +312,7 @@ function activateSelection(mc, selection, clause) {
324
312
  * Process an updated selection value, querying filtered data for any
325
313
  * associated clients.
326
314
  * @param {Coordinator} mc The Mosaic coordinator.
327
- * @param {import('./Selection.js').Selection} selection A selection.
315
+ * @param {Selection} selection A selection.
328
316
  * @returns {Promise} A Promise that resolves when the update completes.
329
317
  */
330
318
  function updateSelection(mc, selection) {
@@ -332,6 +320,7 @@ function updateSelection(mc, selection) {
332
320
  const { clients } = filterGroups.get(selection);
333
321
  const { active } = selection;
334
322
  return Promise.allSettled(Array.from(clients, client => {
323
+ if (!client.enabled) return client.requestQuery();
335
324
  const info = preaggregator.request(client, selection, active);
336
325
  const filter = info ? null : selection.predicate(client);
337
326
 
@@ -1,16 +1,30 @@
1
- import { Coordinator } from './Coordinator.js';
2
- import { Selection } from './Selection.js';
1
+ /** @import { Query } from '@uwdata/mosaic-sql' */
2
+ /** @import { Coordinator } from './Coordinator.js' */
3
+ /** @import { Selection } from './Selection.js' */
3
4
  import { throttle } from './util/throttle.js';
4
5
 
5
6
  /**
6
- * Base class for Mosaic clients.
7
+ * A Mosaic client is a data consumer that indicates its data needs to a
8
+ * Mosaic coordinator via the query method. The coordinator is responsible
9
+ * for issuing queries and returning results to the client.
10
+ *
11
+ * The client life-cycle consists of connection to a coordinator,
12
+ * initialization (potentially involving queries for data schema and summary
13
+ * statistic information), and then interactive queries that may be driven by
14
+ * an associated selection. When no longer needed, a client should be
15
+ * disconnected from the coordinator.
16
+ *
17
+ * When enabled, a client will initialize and respond to query update requests.
18
+ * If disabled, the client will delay initialization and not respond to queries
19
+ * until enabled again. Disabling a client can improve system performance when
20
+ * associated interface elements are offscreen or disabled.
7
21
  */
8
22
  export class MosaicClient {
9
23
  /**
10
- * Constructor.
11
- * @param {*} filterSelection An optional selection to interactively filter
12
- * this client's data. If provided, a coordinator will re-query and update
13
- * the client when the selection updates.
24
+ * Create a new client instance.
25
+ * @param {Selection} [filterSelection] An optional selection to
26
+ * interactively filter this client's data. If provided, a coordinator
27
+ * will re-query and update the client when the selection updates.
14
28
  */
15
29
  constructor(filterSelection) {
16
30
  /** @type {Selection} */
@@ -20,6 +34,12 @@ export class MosaicClient {
20
34
  this._coordinator = null;
21
35
  /** @type {Promise<any>} */
22
36
  this._pending = Promise.resolve();
37
+ /** @type {boolean} */
38
+ this._enabled = true;
39
+ /** @type {boolean} */
40
+ this._initialized = false;
41
+ /** @type {Query | boolean} */
42
+ this._request = null;
23
43
  }
24
44
 
25
45
  /**
@@ -36,6 +56,33 @@ export class MosaicClient {
36
56
  this._coordinator = coordinator;
37
57
  }
38
58
 
59
+ /**
60
+ * Return this client's enabled state.
61
+ */
62
+ get enabled() {
63
+ return this._enabled;
64
+ }
65
+
66
+ /**
67
+ * Set this client's enabled state;
68
+ */
69
+ set enabled(state) {
70
+ state = !!state; // ensure boolean
71
+ if (this._enabled !== state) {
72
+ this._enabled = state;
73
+ if (state) {
74
+ if (!this._initialized) {
75
+ // initialization includes a query request
76
+ this.initialize();
77
+ } else if (this._request) {
78
+ // request query now if requested while disabled
79
+ this.requestQuery(this._request === true ? undefined : this._request);
80
+ }
81
+ this._request = null;
82
+ }
83
+ }
84
+ }
85
+
39
86
  /**
40
87
  * Return a Promise that resolves once the client has updated.
41
88
  */
@@ -52,8 +99,8 @@ export class MosaicClient {
52
99
 
53
100
  /**
54
101
  * Return a boolean indicating if the client query can be sped up with
55
- * materialized views of pre-aggregated data. Should return true if changes to
56
- * the filterBy selection does not change the groupby domain of the client
102
+ * materialized views of pre-aggregated data. Should return true if changes
103
+ * to the filterBy selection do not change the groupby domain of the client
57
104
  * query.
58
105
  */
59
106
  get filterStable() {
@@ -61,25 +108,9 @@ export class MosaicClient {
61
108
  }
62
109
 
63
110
  /**
64
- * Return an array of fields queried by this client.
65
- * @returns {import('./types.js').FieldInfoRequest[] | null}
66
- * The fields to retrieve info for.
67
- */
68
- fields() {
69
- return null;
70
- }
71
-
72
- /**
73
- * Called by the coordinator to set the field info for this client.
74
- * @param {import('./types.js').FieldInfo[]} info The field info result.
75
- * @returns {this}
76
- */
77
- fieldInfo(info) { // eslint-disable-line no-unused-vars
78
- return this;
79
- }
80
-
81
- /**
82
- * Prepare the client before the query() method is called.
111
+ * Prepare the client before the `query()` method is called. Subclasses
112
+ * should override this method as needed, potentially issuing one or more
113
+ * queries to gather data or metadata needed prior to `query` calls.
83
114
  */
84
115
  async prepare() {
85
116
  }
@@ -122,34 +153,55 @@ export class MosaicClient {
122
153
 
123
154
  /**
124
155
  * Request the coordinator to execute a query for this client.
125
- * If an explicit query is not provided, the client query method will
126
- * be called, filtered by the current filterBy selection. This method
127
- * has no effect if the client is not registered with a coordinator.
156
+ * If an explicit query is not provided, the client `query` method will
157
+ * be called, filtered by the current `filterBy` selection. This method has
158
+ * no effect if the client is not connected to a coordinator. If the client
159
+ * is connected by currently disabled, the request will be serviced if the
160
+ * client is later enabled.
161
+ * @param {Query} [query] The query to request. If unspecified, the query
162
+ * will be determind by the client's `query` method and the current
163
+ * `filterBy` selection state.
128
164
  * @returns {Promise}
129
165
  */
130
166
  requestQuery(query) {
131
- const q = query || this.query(this.filterBy?.predicate(this));
132
- return this._coordinator?.requestQuery(this, q);
167
+ if (this._enabled) {
168
+ const q = query || this.query(this.filterBy?.predicate(this));
169
+ return this._coordinator?.requestQuery(this, q);
170
+ } else {
171
+ this._request = query ?? true;
172
+ return null;
173
+ }
133
174
  }
134
175
 
135
176
  /**
136
177
  * Request that the coordinator perform a throttled update of this client
137
- * using the default query. Unlike requestQuery, for which every call will
138
- * result in an executed query, multiple calls to requestUpdate may be
139
- * consolidated into a single update.
178
+ * using the default query. Unlike requestQuery, for which every call results
179
+ * in an executed query, multiple calls to requestUpdate may be consolidated
180
+ * into a single update. This method has no effect if the client is not
181
+ * connected to a coordinator. If the client is connected but currently
182
+ * disabled, the request will be serviced if the client is later enabled.
140
183
  */
141
184
  requestUpdate() {
142
- this._requestUpdate();
185
+ if (this._enabled) {
186
+ this._requestUpdate();
187
+ } else {
188
+ this.requestQuery();
189
+ }
143
190
  }
144
191
 
145
192
  /**
146
- * Reset this client, initiating new field info, call the prepare method,
147
- * and query requests. This method has no effect if the client is not
148
- * registered with a coordinator.
149
- * @returns {Promise}
193
+ * Reset this client, calling the prepare method and query requests. This
194
+ * method has no effect if the client is not registered with a coordinator.
150
195
  */
151
196
  initialize() {
152
- return this._coordinator?.initializeClient(this);
197
+ if (!this._enabled) {
198
+ // clear flag so we initialize when enabled again
199
+ this._initialized = false;
200
+ } else if (this._coordinator) {
201
+ // if connected, let's initialize
202
+ this._initialized = true;
203
+ this._pending = this.prepare().then(() => this.requestQuery());
204
+ }
153
205
  }
154
206
 
155
207
  /**
@@ -1,64 +1,105 @@
1
- import { MosaicClient } from "./MosaicClient.js";
2
- import {
3
- coordinator as defaultCoordinator,
4
- } from "./Coordinator.js";
1
+ /** @import { Coordinator } from './Coordinator.js' */
2
+ /** @import { Selection } from './Selection.js' */
3
+ import { MosaicClient } from './MosaicClient.js';
4
+ import { coordinator as defaultCoordinator } from './Coordinator.js';
5
5
 
6
6
  /**
7
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.
8
+ * @property {Coordinator} [coordinator] Mosaic coordinator.
9
+ * Defaults to the global coordinator.
10
+ * @property {Selection|null} [selection] A selection whose predicates are
11
+ * fed into the query function to produce the SQL query.
12
+ * @property {boolean} [enabled] A flag (default `true`) indicating if the
13
+ * client should initially be enabled or not.
14
+ * @property {boolean} [filterStable] A flag (default `true`) indicating if the
15
+ * if client queries can be sped up using pre-aggregated data. Should be set
16
+ * to `false` if filtering changes the groupby domain of the query.
17
+ * @property {function(): Promise<void>} [prepare]
18
+ * An async function to prepare the client before running queries.
19
+ * @property {function(any): any} [query]
20
+ * A function that returns a query from a list of selection predicates.
21
+ * @property {function(any): void} [queryResult]
22
+ * Called by the coordinator to return a query result.
23
+ * @property {function(): void} [queryPending]
24
+ * Called by the coordinator to report a query execution error.
25
+ * @property {function(any): void} [queryError]
26
+ * Called by the coordinator to inform the client that a query is pending.
15
27
  */
16
28
 
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.
29
+ /**
30
+ * Make a new client with the given options, and connect the client to the
31
+ * provided coordinator.
32
+ * @param {MakeClientOptions} options The options for making the client.
33
+ * @returns {MosaicClient & { destroy: () => void }} The resulting client,
34
+ * along with a method to destroy the client when no longer needed.
20
35
  */
21
36
  export function makeClient(options) {
22
- const coordinator = options.coordinator ?? defaultCoordinator();
23
- const client = new ProxyClient({ ...options, coordinator });
37
+ const {
38
+ coordinator = defaultCoordinator(),
39
+ ...clientOptions
40
+ } = options;
41
+ const client = new ProxyClient(clientOptions);
24
42
  coordinator.connect(client);
25
43
  return client;
26
44
  }
27
45
 
28
- /** An internal class used to implement the makeClient API */
46
+ /**
47
+ * An internal class used to implement the makeClient API.
48
+ */
29
49
  class ProxyClient extends MosaicClient {
30
- /** @param {MakeClientOptions} options */
31
- constructor(options) {
32
- super(options.selection);
50
+ /**
51
+ * @param {MakeClientOptions} options The options for making the client.
52
+ */
53
+ constructor({
54
+ selection = undefined,
55
+ enabled = true,
56
+ filterStable = true,
57
+ ...methods
58
+ }) {
59
+ super(selection);
60
+ this.enabled = enabled;
61
+
62
+ /**
63
+ * @type {MakeClientOptions}
64
+ * @readonly
65
+ */
66
+ this._methods = methods;
67
+
68
+ /**
69
+ * @type {boolean}
70
+ * @readonly
71
+ */
72
+ this._filterStable = filterStable;
73
+ }
33
74
 
34
- /** @type {MakeClientOptions} */
35
- this._options = { ...options };
75
+ get filterStable() {
76
+ return this._filterStable;
36
77
  }
37
78
 
38
79
  async prepare() {
39
- await this._options.prepare?.();
80
+ await this._methods.prepare?.();
40
81
  }
41
82
 
42
83
  query(filter) {
43
- return this._options.query(filter);
84
+ return this._methods.query?.(filter) ?? null;
44
85
  }
45
86
 
46
87
  queryResult(data) {
47
- this._options.queryResult?.(data);
88
+ this._methods.queryResult?.(data);
48
89
  return this;
49
90
  }
50
91
 
51
92
  queryPending() {
52
- this._options.queryPending?.();
93
+ this._methods.queryPending?.();
53
94
  return this;
54
95
  }
55
96
 
56
97
  queryError(error) {
57
- this._options.queryError?.(error);
98
+ this._methods.queryError?.(error);
58
99
  return this;
59
100
  }
60
101
 
61
102
  destroy() {
62
- this._options.coordinator.disconnect(this);
103
+ this.coordinator.disconnect(this);
63
104
  }
64
105
  }
@@ -1,4 +1,9 @@
1
- import { Query, and, asNode, ceil, collectColumns, createTable, float64, floor, isBetween, int32, mul, round, scaleTransform, sub, isSelectQuery, ExprNode, SelectQuery, isAggregateExpression, ColumnNameRefNode } from '@uwdata/mosaic-sql';
1
+ /** @import { ExprNode } from '@uwdata/mosaic-sql' */
2
+ /** @import { Coordinator } from '../Coordinator.js' */
3
+ /** @import { MosaicClient } from '../MosaicClient.js' */
4
+ /** @import { Selection } from '../Selection.js' */
5
+ /** @import { BinMethod, Scale, SelectionClause } from '../util/selection-types.js' */
6
+ import { Query, and, asNode, ceil, collectColumns, createTable, float64, floor, isBetween, int32, mul, round, scaleTransform, sub, isSelectQuery, SelectQuery, isAggregateExpression, ColumnNameRefNode } from '@uwdata/mosaic-sql';
2
7
  import { preaggColumns } from './preagg-columns.js';
3
8
  import { fnv_hash } from '../util/hash.js';
4
9
 
@@ -34,14 +39,14 @@ const Skip = { skip: true, result: null };
34
39
  export class PreAggregator {
35
40
  /**
36
41
  * Create a new manager of materialized views of pre-aggregated data.
37
- * @param {import('../Coordinator.js').Coordinator} coordinator A Mosaic coordinator.
42
+ * @param {Coordinator} coordinator A Mosaic coordinator.
38
43
  * @param {PreAggregateOptions} [options] Pre-aggregation options.
39
44
  */
40
45
  constructor(coordinator, {
41
46
  schema = 'mosaic',
42
47
  enabled = true
43
48
  } = {}) {
44
- /** @type {Map<import('../MosaicClient.js').MosaicClient, PreAggregateInfo | Skip | null>} */
49
+ /** @type {Map<MosaicClient, PreAggregateInfo | Skip | null>} */
45
50
  this.entries = new Map();
46
51
  this.active = null;
47
52
  this.mc = coordinator;
@@ -123,18 +128,16 @@ export class PreAggregator {
123
128
  * client-selection pair, or null if the client has unstable filters.
124
129
  * This method has multiple possible side effects, including materialized
125
130
  * view creation and updating internal caches.
126
- * @param {import('../MosaicClient.js').MosaicClient} client A Mosaic client.
127
- * @param {import('../Selection.js').Selection} selection A Mosaic selection
128
- * to filter the client by.
129
- * @param {import('../util/selection-types.js').SelectionClause} activeClause
130
- * A representative active selection clause for which to (possibly) generate
131
- * materialized views of pre-aggregates.
131
+ * @param {MosaicClient} client A Mosaic client.
132
+ * @param {Selection} selection A Mosaic selection to filter the client by.
133
+ * @param {SelectionClause} activeClause A representative active selection
134
+ * clause for which to generate materialized views of pre-aggregates.
132
135
  * @returns {PreAggregateInfo | Skip | null} Information and query generator
133
136
  * for pre-aggregated tables, or null if the client has unstable filters.
134
137
  */
135
138
  request(client, selection, activeClause) {
136
139
  // if not enabled, do nothing
137
- if (!this.enabled) return null;
140
+ if (!this.enabled || activeClause == null) return null;
138
141
 
139
142
  const { entries, mc, schema } = this;
140
143
  const { source } = activeClause;
@@ -205,8 +208,7 @@ export class PreAggregator {
205
208
  * function for the active dimensions of a pre-aggregated materialized view.
206
209
  * If the active clause is not indexable or is missing metadata, this method
207
210
  * returns an object with a null source property.
208
- * @param {import('../util/selection-types.js').SelectionClause} clause
209
- * The active selection clause to analyze.
211
+ * @param {SelectionClause} clause The active selection clause to analyze.
210
212
  */
211
213
  function activeColumns(clause) {
212
214
  const { source, meta } = clause;
@@ -261,12 +263,12 @@ const BIN = { ceil, round };
261
263
 
262
264
  /**
263
265
  * Returns a bin function generator to discretize a selection interval domain.
264
- * @param {import('../util/selection-types.js').Scale} scale A scale that maps
265
- * domain values to the output range (typically pixels).
266
+ * @param {Scale} scale A scale that maps domain values to the output range
267
+ * (typically pixels).
266
268
  * @param {number} pixelSize The interactive pixel size. This value indicates
267
269
  * the bin step size and may be greater than an actual screen pixel.
268
- * @param {import('../util/selection-types.js').BinMethod} bin The binning
269
- * method to apply, one of `floor`, `ceil', or `round`.
270
+ * @param {BinMethod} bin The binning method to apply, one of `floor`,
271
+ * `ceil', or `round`.
270
272
  * @returns {(value: any) => ExprNode} A bin function generator.
271
273
  */
272
274
  function binInterval(scale, pixelSize, bin) {
@@ -426,8 +428,7 @@ export class PreAggregateInfo {
426
428
 
427
429
  /**
428
430
  * Generate a materialized view query for the given predicate.
429
- * @param {import('@uwdata/mosaic-sql').ExprNode} predicate The current
430
- * active clause predicate.
431
+ * @param {ExprNode} predicate The current active clause predicate.
431
432
  * @returns {SelectQuery} A materialized view query.
432
433
  */
433
434
  query(predicate) {