@uwdata/mosaic-core 0.17.0 → 0.18.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/LICENSE +47 -0
- package/README.md +0 -1
- package/dist/src/Coordinator.d.ts +147 -0
- package/dist/src/Coordinator.d.ts.map +1 -0
- package/dist/src/Coordinator.js +269 -0
- package/dist/src/Coordinator.js.map +1 -0
- package/dist/src/MosaicClient.d.ts +138 -0
- package/dist/src/MosaicClient.d.ts.map +1 -0
- package/dist/src/MosaicClient.js +213 -0
- package/dist/src/MosaicClient.js.map +1 -0
- package/dist/src/Param.d.ts +56 -0
- package/dist/src/Param.d.ts.map +1 -0
- package/dist/src/Param.js +89 -0
- package/dist/src/Param.js.map +1 -0
- package/dist/src/QueryConsolidator.d.ts +11 -0
- package/dist/src/QueryConsolidator.d.ts.map +1 -0
- package/dist/src/QueryConsolidator.js +249 -0
- package/dist/src/QueryConsolidator.js.map +1 -0
- package/dist/src/QueryManager.d.ts +77 -0
- package/dist/src/QueryManager.d.ts.map +1 -0
- package/dist/src/QueryManager.js +174 -0
- package/dist/src/QueryManager.js.map +1 -0
- package/dist/src/Selection.d.ts +222 -0
- package/dist/src/Selection.d.ts.map +1 -0
- package/dist/src/Selection.js +319 -0
- package/dist/src/Selection.js.map +1 -0
- package/dist/src/SelectionClause.d.ts +192 -0
- package/dist/src/SelectionClause.d.ts.map +1 -0
- package/dist/src/SelectionClause.js +126 -0
- package/dist/src/SelectionClause.js.map +1 -0
- package/dist/src/connectors/Connector.d.ts +26 -0
- package/dist/src/connectors/Connector.d.ts.map +1 -0
- package/dist/src/connectors/Connector.js +2 -0
- package/dist/src/connectors/Connector.js.map +1 -0
- package/dist/src/connectors/rest.d.ts +24 -0
- package/dist/src/connectors/rest.d.ts.map +1 -0
- package/dist/src/connectors/rest.js +37 -0
- package/dist/src/connectors/rest.js.map +1 -0
- package/dist/src/connectors/socket.d.ts +40 -0
- package/dist/src/connectors/socket.d.ts.map +1 -0
- package/dist/src/connectors/socket.js +115 -0
- package/dist/src/connectors/socket.js.map +1 -0
- package/dist/src/connectors/wasm.d.ts +53 -0
- package/dist/src/connectors/wasm.d.ts.map +1 -0
- package/dist/src/connectors/wasm.js +113 -0
- package/dist/src/connectors/wasm.js.map +1 -0
- package/dist/src/index.d.ts +28 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +25 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/make-client.d.ts +35 -0
- package/dist/src/make-client.d.ts.map +1 -0
- package/dist/src/make-client.js +52 -0
- package/dist/src/make-client.js.map +1 -0
- package/dist/src/preagg/PreAggregator.d.ts +150 -0
- package/dist/src/preagg/PreAggregator.d.ts.map +1 -0
- package/dist/src/preagg/PreAggregator.js +382 -0
- package/dist/src/preagg/PreAggregator.js.map +1 -0
- package/dist/src/preagg/preagg-columns.d.ts +16 -0
- package/dist/src/preagg/preagg-columns.d.ts.map +1 -0
- package/dist/src/preagg/preagg-columns.js +95 -0
- package/dist/src/preagg/preagg-columns.js.map +1 -0
- package/dist/src/preagg/sufficient-statistics.d.ts +14 -0
- package/dist/src/preagg/sufficient-statistics.d.ts.map +1 -0
- package/dist/src/preagg/sufficient-statistics.js +446 -0
- package/dist/src/preagg/sufficient-statistics.js.map +1 -0
- package/dist/src/types.d.ts +77 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/util/AsyncDispatch.d.ts +121 -0
- package/dist/src/util/AsyncDispatch.d.ts.map +1 -0
- package/dist/src/util/AsyncDispatch.js +188 -0
- package/dist/src/util/AsyncDispatch.js.map +1 -0
- package/dist/src/util/cache.d.ts +19 -0
- package/dist/src/util/cache.d.ts.map +1 -0
- package/dist/src/util/cache.js +66 -0
- package/dist/src/util/cache.js.map +1 -0
- package/dist/src/util/decode-ipc.d.ts +12 -0
- package/dist/src/util/decode-ipc.d.ts.map +1 -0
- package/{src → dist/src}/util/decode-ipc.js +5 -6
- package/dist/src/util/decode-ipc.js.map +1 -0
- package/dist/src/util/distinct.d.ts +3 -0
- package/dist/src/util/distinct.d.ts.map +1 -0
- package/dist/src/util/distinct.js +16 -0
- package/dist/src/util/distinct.js.map +1 -0
- package/dist/src/util/field-info.d.ts +26 -0
- package/dist/src/util/field-info.d.ts.map +1 -0
- package/dist/src/util/field-info.js +91 -0
- package/dist/src/util/field-info.js.map +1 -0
- package/dist/src/util/hash.d.ts +2 -0
- package/dist/src/util/hash.d.ts.map +1 -0
- package/dist/src/util/hash.js +26 -0
- package/dist/src/util/hash.js.map +1 -0
- package/dist/src/util/is-activatable.d.ts +8 -0
- package/dist/src/util/is-activatable.d.ts.map +1 -0
- package/dist/src/util/is-activatable.js +10 -0
- package/dist/src/util/is-activatable.js.map +1 -0
- package/dist/src/util/is-arrow-table.d.ts +9 -0
- package/dist/src/util/is-arrow-table.d.ts.map +1 -0
- package/dist/src/util/is-arrow-table.js +11 -0
- package/dist/src/util/is-arrow-table.js.map +1 -0
- package/dist/src/util/js-type.d.ts +9 -0
- package/dist/src/util/js-type.d.ts.map +1 -0
- package/dist/src/util/js-type.js +59 -0
- package/dist/src/util/js-type.js.map +1 -0
- package/dist/src/util/priority-queue.d.ts +35 -0
- package/dist/src/util/priority-queue.d.ts.map +1 -0
- package/dist/src/util/priority-queue.js +81 -0
- package/dist/src/util/priority-queue.js.map +1 -0
- package/dist/src/util/query-result.d.ts +47 -0
- package/dist/src/util/query-result.d.ts.map +1 -0
- package/dist/src/util/query-result.js +83 -0
- package/dist/src/util/query-result.js.map +1 -0
- package/dist/src/util/synchronizer.d.ts +36 -0
- package/dist/src/util/synchronizer.d.ts.map +1 -0
- package/dist/src/util/synchronizer.js +52 -0
- package/dist/src/util/synchronizer.js.map +1 -0
- package/dist/src/util/throttle.d.ts +12 -0
- package/dist/src/util/throttle.d.ts.map +1 -0
- package/dist/src/util/throttle.js +51 -0
- package/dist/src/util/throttle.js.map +1 -0
- package/dist/src/util/to-data-columns.d.ts +22 -0
- package/dist/src/util/to-data-columns.d.ts.map +1 -0
- package/dist/src/util/to-data-columns.js +51 -0
- package/dist/src/util/to-data-columns.js.map +1 -0
- package/dist/src/util/void-logger.d.ts +13 -0
- package/dist/src/util/void-logger.d.ts.map +1 -0
- package/dist/src/util/void-logger.js +13 -0
- package/dist/src/util/void-logger.js.map +1 -0
- package/package.json +16 -10
- package/src/Coordinator.ts +367 -0
- package/src/{MosaicClient.js → MosaicClient.ts} +49 -43
- package/src/{Param.js → Param.ts} +29 -28
- package/src/{QueryConsolidator.js → QueryConsolidator.ts} +81 -58
- package/src/{QueryManager.js → QueryManager.ts} +61 -54
- package/src/Selection.ts +388 -0
- package/src/SelectionClause.ts +275 -0
- package/src/connectors/Connector.ts +6 -6
- package/src/connectors/rest.ts +56 -0
- package/src/connectors/{socket.js → socket.ts} +53 -42
- package/src/connectors/{wasm.js → wasm.ts} +46 -62
- package/src/{index.js → index.ts} +13 -1
- package/src/make-client.ts +93 -0
- package/src/preagg/{PreAggregator.js → PreAggregator.ts} +164 -145
- package/src/preagg/{preagg-columns.js → preagg-columns.ts} +27 -24
- package/src/preagg/{sufficient-statistics.js → sufficient-statistics.ts} +160 -110
- package/src/types.ts +24 -9
- package/src/util/{AsyncDispatch.js → AsyncDispatch.ts} +62 -43
- package/src/util/{cache.js → cache.ts} +25 -15
- package/src/util/decode-ipc.ts +15 -0
- package/src/util/{distinct.js → distinct.ts} +3 -3
- package/src/util/{field-info.js → field-info.ts} +31 -32
- package/src/util/{hash.js → hash.ts} +4 -4
- package/src/util/is-activatable.ts +11 -0
- package/src/util/is-arrow-table.ts +12 -0
- package/src/util/{js-type.js → js-type.ts} +7 -5
- package/src/util/{priority-queue.js → priority-queue.ts} +32 -20
- package/src/util/{query-result.js → query-result.ts} +24 -17
- package/src/util/synchronizer.ts +56 -0
- package/src/util/throttle.ts +59 -0
- package/src/util/to-data-columns.ts +65 -0
- package/src/util/void-logger.ts +23 -0
- package/src/Coordinator.js +0 -313
- package/src/Selection.js +0 -380
- package/src/SelectionClause.js +0 -159
- package/src/connectors/rest.js +0 -38
- package/src/index-types.ts +0 -5
- package/src/make-client.js +0 -101
- package/src/util/is-activatable.js +0 -8
- package/src/util/is-arrow-table.js +0 -10
- package/src/util/selection-types.ts +0 -137
- package/src/util/synchronizer.js +0 -47
- package/src/util/throttle.js +0 -54
- package/src/util/to-data-columns.js +0 -60
- package/src/util/void-logger.js +0 -13
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"to-data-columns.js","sourceRoot":"","sources":["../../../src/util/to-data-columns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAiBnD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,IAAW;IACjC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,EAA8B,EAAE,CAAC;AAC5E,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,cAAc,CAAC,IAA8B;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,MAAM,OAAO,GAA8B,EAAE,CAAC;QAC9C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBACnB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACnC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface Logger {
|
|
2
|
+
debug(...args: unknown[]): void;
|
|
3
|
+
info(...args: unknown[]): void;
|
|
4
|
+
log(...args: unknown[]): void;
|
|
5
|
+
warn(...args: unknown[]): void;
|
|
6
|
+
error(...args: unknown[]): void;
|
|
7
|
+
group(label?: string): void;
|
|
8
|
+
groupCollapsed(label?: string): void;
|
|
9
|
+
groupEnd(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function voidLogger(): Logger;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=void-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"void-logger.d.ts","sourceRoot":"","sources":["../../../src/util/void-logger.ts"],"names":[],"mappings":"AAAA,UAAU,MAAM;IACd,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC9B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,wBAAgB,UAAU,IAAI,MAAM,CAWnC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"void-logger.js","sourceRoot":"","sources":["../../../src/util/void-logger.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,KAAK,KAAU,CAAC;QAChB,IAAI,KAAU,CAAC;QACf,GAAG,KAAU,CAAC;QACd,IAAI,KAAU,CAAC;QACf,KAAK,KAAU,CAAC;QAChB,KAAK,KAAU,CAAC;QAChB,cAAc,KAAU,CAAC;QACzB,QAAQ,KAAU,CAAC;KACpB,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uwdata/mosaic-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Scalable and extensible linked data views.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mosaic",
|
|
@@ -13,25 +13,31 @@
|
|
|
13
13
|
"author": "Jeffrey Heer (https://idl.uw.edu)",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"exports": {
|
|
16
|
-
"types": "./dist/src/index
|
|
17
|
-
"default": "./src/index.js"
|
|
16
|
+
"types": "./dist/src/index.d.ts",
|
|
17
|
+
"default": "./dist/src/index.js"
|
|
18
18
|
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"!dist/tsconfig.tsbuildinfo",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
19
24
|
"repository": {
|
|
20
25
|
"type": "git",
|
|
21
26
|
"url": "https://github.com/uwdata/mosaic.git"
|
|
22
27
|
},
|
|
23
28
|
"scripts": {
|
|
24
|
-
"
|
|
29
|
+
"clean": "rimraf dist && mkdir dist",
|
|
25
30
|
"lint": "eslint src test",
|
|
26
|
-
"test": "vitest run
|
|
27
|
-
"prepublishOnly": "npm run test && npm run lint && tsc"
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"prepublishOnly": "npm run test && npm run lint && tsc --build"
|
|
28
33
|
},
|
|
29
34
|
"dependencies": {
|
|
30
35
|
"@duckdb/duckdb-wasm": "^1.29.0",
|
|
31
|
-
"@uwdata/flechette": "^2.0.
|
|
32
|
-
"@uwdata/mosaic-sql": "^0.
|
|
36
|
+
"@uwdata/flechette": "^2.0.1",
|
|
37
|
+
"@uwdata/mosaic-sql": "^0.18.0"
|
|
33
38
|
},
|
|
34
39
|
"devDependencies": {
|
|
35
|
-
"@uwdata/mosaic-duckdb": "^0.
|
|
36
|
-
}
|
|
40
|
+
"@uwdata/mosaic-duckdb": "^0.18.0"
|
|
41
|
+
},
|
|
42
|
+
"gitHead": "dfb9ded0b0307754e3185ca34cc49a1384fe8455"
|
|
37
43
|
}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { SocketConnector } from './connectors/socket.js';
|
|
2
|
+
import { type Connector } from './connectors/Connector.js';
|
|
3
|
+
import { PreAggregator, type PreAggregateOptions } from './preagg/PreAggregator.js';
|
|
4
|
+
import { voidLogger } from './util/void-logger.js';
|
|
5
|
+
import { QueryManager, Priority } from './QueryManager.js';
|
|
6
|
+
import { type Selection } from './Selection.js';
|
|
7
|
+
import { type Logger, type QueryType } from './types.js';
|
|
8
|
+
import { type QueryResult } from './util/query-result.js';
|
|
9
|
+
import { type MosaicClient } from './MosaicClient.js';
|
|
10
|
+
import { type SelectionClause } from './SelectionClause.js';
|
|
11
|
+
import { MaybeArray } from '@uwdata/mosaic-sql';
|
|
12
|
+
|
|
13
|
+
interface FilterGroupEntry {
|
|
14
|
+
selection: Selection;
|
|
15
|
+
clients: Set<MosaicClient>;
|
|
16
|
+
disconnect(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The singleton Coordinator instance.
|
|
21
|
+
*/
|
|
22
|
+
let _instance: Coordinator;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Set or retrieve the coordinator instance.
|
|
26
|
+
* @param instance The coordinator instance to set
|
|
27
|
+
* @returns The coordinator instance
|
|
28
|
+
*/
|
|
29
|
+
export function coordinator(
|
|
30
|
+
instance?: Coordinator
|
|
31
|
+
): Coordinator {
|
|
32
|
+
if (instance) {
|
|
33
|
+
_instance = instance;
|
|
34
|
+
} else if (_instance == null) {
|
|
35
|
+
_instance = new Coordinator();
|
|
36
|
+
}
|
|
37
|
+
return _instance;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A Mosaic Coordinator manages all database communication for clients and
|
|
42
|
+
* handles selection updates. The Coordinator also performs optimizations
|
|
43
|
+
* including query caching, consolidation, and pre-aggregation.
|
|
44
|
+
*/
|
|
45
|
+
export class Coordinator {
|
|
46
|
+
public manager: QueryManager;
|
|
47
|
+
public preaggregator: PreAggregator;
|
|
48
|
+
public clients = new Set<MosaicClient>;
|
|
49
|
+
public filterGroups = new Map<Selection, FilterGroupEntry>;
|
|
50
|
+
protected _logger: Logger = voidLogger();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param db Database connector. Defaults to a web socket connection.
|
|
54
|
+
* @param options Coordinator options.
|
|
55
|
+
* @param options.logger The logger to use, defaults to `console`.
|
|
56
|
+
* @param options.manager The query manager to use.
|
|
57
|
+
* @param options.cache Boolean flag to enable/disable query caching.
|
|
58
|
+
* @param options.consolidate Boolean flag to enable/disable query consolidation.
|
|
59
|
+
* @param options.preagg Options for the Pre-aggregator.
|
|
60
|
+
*/
|
|
61
|
+
constructor(
|
|
62
|
+
db: Connector = new SocketConnector(),
|
|
63
|
+
options: {
|
|
64
|
+
logger?: Logger | null;
|
|
65
|
+
manager?: QueryManager;
|
|
66
|
+
cache?: boolean;
|
|
67
|
+
consolidate?: boolean;
|
|
68
|
+
preagg?: PreAggregateOptions;
|
|
69
|
+
} = {}
|
|
70
|
+
) {
|
|
71
|
+
const {
|
|
72
|
+
logger = console,
|
|
73
|
+
manager = new QueryManager(),
|
|
74
|
+
cache = true,
|
|
75
|
+
consolidate = true,
|
|
76
|
+
preagg = {}
|
|
77
|
+
} = options;
|
|
78
|
+
this.manager = manager;
|
|
79
|
+
this.manager.cache(cache);
|
|
80
|
+
this.manager.consolidate(consolidate);
|
|
81
|
+
this.databaseConnector(db);
|
|
82
|
+
this.logger(logger);
|
|
83
|
+
this.clear();
|
|
84
|
+
this.preaggregator = new PreAggregator(this, preagg);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Clear the coordinator state.
|
|
89
|
+
* @param options Options object.
|
|
90
|
+
* @param options.clients If true, disconnect all clients.
|
|
91
|
+
* @param options.cache If true, clear the query cache.
|
|
92
|
+
*/
|
|
93
|
+
clear(options: { clients?: boolean; cache?: boolean } = {}) {
|
|
94
|
+
const { clients = true, cache = true } = options;
|
|
95
|
+
this.manager.clear();
|
|
96
|
+
if (clients) {
|
|
97
|
+
this.filterGroups?.forEach(group => group.disconnect());
|
|
98
|
+
this.filterGroups = new Map;
|
|
99
|
+
this.clients?.forEach(client => this.disconnect(client));
|
|
100
|
+
this.clients = new Set;
|
|
101
|
+
}
|
|
102
|
+
if (cache) this.manager.cache()!.clear();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get or set the database connector.
|
|
107
|
+
* @param db The database connector to use.
|
|
108
|
+
* @returns The current database connector.
|
|
109
|
+
*/
|
|
110
|
+
databaseConnector(): Connector | null;
|
|
111
|
+
databaseConnector(db: Connector): Connector;
|
|
112
|
+
databaseConnector(db?: Connector): Connector | null {
|
|
113
|
+
return db
|
|
114
|
+
? this.manager.connector(db)
|
|
115
|
+
: this.manager.connector();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get or set the logger.
|
|
120
|
+
* @param logger The logger to use.
|
|
121
|
+
* @returns The current logger
|
|
122
|
+
*/
|
|
123
|
+
logger(logger?: Logger | null): Logger {
|
|
124
|
+
if (arguments.length) {
|
|
125
|
+
this._logger = logger || voidLogger();
|
|
126
|
+
this.manager.logger(this._logger);
|
|
127
|
+
}
|
|
128
|
+
return this._logger!;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// -- Query Management ----
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Cancel previously submitted query requests. These queries will be
|
|
135
|
+
* canceled if they are queued but have not yet been submitted.
|
|
136
|
+
* @param requests An array of query result objects, such as those returned by the `query` method.
|
|
137
|
+
*/
|
|
138
|
+
cancel(requests: QueryResult[]) {
|
|
139
|
+
this.manager.cancel(requests);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Issue a query for which no result (return value) is needed.
|
|
144
|
+
* @param query The query or an array of queries. Each query should be either a Query builder object or a SQL string.
|
|
145
|
+
* @param options An options object.
|
|
146
|
+
* @param options.priority The query priority, defaults to `Priority.Normal`.
|
|
147
|
+
* @returns A query result promise.
|
|
148
|
+
*/
|
|
149
|
+
exec(
|
|
150
|
+
query: MaybeArray<QueryType>,
|
|
151
|
+
options: { priority?: number } = {}
|
|
152
|
+
): QueryResult {
|
|
153
|
+
const { priority = Priority.Normal } = options;
|
|
154
|
+
query = Array.isArray(query) ? query.filter(x => x).join(';\n') : query;
|
|
155
|
+
return this.manager.request({ type: 'exec', query }, priority);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Issue a query to the backing database. The submitted query may be
|
|
160
|
+
* consolidate with other queries and its results may be cached.
|
|
161
|
+
* @param query The query as either a Query builder object or a SQL string.
|
|
162
|
+
* @param options An options object.
|
|
163
|
+
* @param options.type The query result format type.
|
|
164
|
+
* @param options.cache If true, cache the query result client-side within the QueryManager.
|
|
165
|
+
* @param options.persist If true, request the database server to persist a cached query server-side.
|
|
166
|
+
* @param options.priority The query priority, defaults to `Priority.Normal`.
|
|
167
|
+
* @returns A query result promise.
|
|
168
|
+
*/
|
|
169
|
+
query(
|
|
170
|
+
query: QueryType,
|
|
171
|
+
options: {
|
|
172
|
+
type?: 'arrow' | 'json';
|
|
173
|
+
cache?: boolean;
|
|
174
|
+
persist?: boolean;
|
|
175
|
+
priority?: number;
|
|
176
|
+
[key: string]: unknown;
|
|
177
|
+
} = {}
|
|
178
|
+
): QueryResult {
|
|
179
|
+
const {
|
|
180
|
+
type = 'arrow',
|
|
181
|
+
cache = true,
|
|
182
|
+
priority = Priority.Normal,
|
|
183
|
+
...otherOptions
|
|
184
|
+
} = options;
|
|
185
|
+
return this.manager.request({ type, query, cache, options: otherOptions }, priority);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Issue a query to prefetch data for later use. The query result is cached
|
|
190
|
+
* for efficient future access.
|
|
191
|
+
* @param query The query as either a Query builder object or a SQL string.
|
|
192
|
+
* @param options An options object.
|
|
193
|
+
* @param options.type The query result format type.
|
|
194
|
+
* @returns A query result promise.
|
|
195
|
+
*/
|
|
196
|
+
prefetch(
|
|
197
|
+
query: QueryType,
|
|
198
|
+
options: { type?: 'arrow' | 'json'; [key: string]: unknown } = {}
|
|
199
|
+
): QueryResult {
|
|
200
|
+
return this.query(query, { ...options, cache: true, priority: Priority.Low });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// -- Client Management ----
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Update client data by submitting the given query and returning the()
|
|
207
|
+
* data (or error) to the client.
|
|
208
|
+
* @param client A Mosaic client.
|
|
209
|
+
* @param query The data query.
|
|
210
|
+
* @param priority The query priority.
|
|
211
|
+
* @returns A Promise that resolves upon completion of the update.
|
|
212
|
+
*/
|
|
213
|
+
updateClient(
|
|
214
|
+
client: MosaicClient,
|
|
215
|
+
query: QueryType,
|
|
216
|
+
priority: number = Priority.Normal
|
|
217
|
+
): Promise<unknown> {
|
|
218
|
+
client.queryPending();
|
|
219
|
+
return client._pending = this.query(query, { priority })
|
|
220
|
+
.then(
|
|
221
|
+
data => client.queryResult(data).update(),
|
|
222
|
+
err => { this._logger?.error(err); client.queryError(err); }
|
|
223
|
+
)
|
|
224
|
+
.catch(err => this._logger?.error(err));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Issue a query request for a client. If the query is null or undefined,
|
|
229
|
+
* the client is simply updated. Otherwise `updateClient` is called. As a
|
|
230
|
+
* side effect, this method clears the current preaggregator state.
|
|
231
|
+
* @param client The client to update.
|
|
232
|
+
* @param query The query to issue.
|
|
233
|
+
*/
|
|
234
|
+
requestQuery(client: MosaicClient, query?: QueryType | null): Promise<unknown> {
|
|
235
|
+
this.preaggregator.clear();
|
|
236
|
+
return query
|
|
237
|
+
? this.updateClient(client, query)
|
|
238
|
+
: Promise.resolve(client.update());
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Connect a client to the coordinator.
|
|
243
|
+
* Throws an error if the client is already connected.
|
|
244
|
+
* @param client The Mosaic client to connect.
|
|
245
|
+
*/
|
|
246
|
+
connect(client: MosaicClient): void {
|
|
247
|
+
const { clients } = this;
|
|
248
|
+
|
|
249
|
+
if (clients?.has(client)) {
|
|
250
|
+
throw new Error('Client already connected.');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// add client to client set
|
|
254
|
+
clients?.add(client);
|
|
255
|
+
|
|
256
|
+
// register coordinator on client instance
|
|
257
|
+
client.coordinator = this;
|
|
258
|
+
|
|
259
|
+
// initialize client lifecycle
|
|
260
|
+
client.initialize();
|
|
261
|
+
|
|
262
|
+
// connect filter selection
|
|
263
|
+
connectSelection(this, client.filterBy!, client);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Disconnect a client from the coordinator.
|
|
268
|
+
* This method has no effect if the client is already disconnected.
|
|
269
|
+
* @param client The Mosaic client to disconnect.
|
|
270
|
+
*/
|
|
271
|
+
disconnect(client: MosaicClient): void {
|
|
272
|
+
const { clients, filterGroups } = this;
|
|
273
|
+
if (!clients?.has(client)) return;
|
|
274
|
+
clients.delete(client);
|
|
275
|
+
client.coordinator = null;
|
|
276
|
+
|
|
277
|
+
const group = filterGroups?.get(client.filterBy!);
|
|
278
|
+
if (group) {
|
|
279
|
+
group.clients.delete(client);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Connect a selection-client pair to the coordinator to process updates.
|
|
286
|
+
* @param mc The Mosaic coordinator.
|
|
287
|
+
* @param selection A selection.
|
|
288
|
+
* @param client A Mosaic client that is filtered by the given selection.
|
|
289
|
+
*/
|
|
290
|
+
function connectSelection(
|
|
291
|
+
mc: Coordinator,
|
|
292
|
+
selection: Selection,
|
|
293
|
+
client: MosaicClient
|
|
294
|
+
): void {
|
|
295
|
+
if (!selection) return;
|
|
296
|
+
let entry = mc.filterGroups?.get(selection);
|
|
297
|
+
if (!entry) {
|
|
298
|
+
const activate = (clause: SelectionClause) => activateSelection(mc, selection, clause);
|
|
299
|
+
const value = () => updateSelection(mc, selection);
|
|
300
|
+
|
|
301
|
+
// @ts-expect-error todo: update selection dispatch types
|
|
302
|
+
selection.addEventListener('activate', activate);
|
|
303
|
+
selection.addEventListener('value', value);
|
|
304
|
+
|
|
305
|
+
entry = {
|
|
306
|
+
selection,
|
|
307
|
+
clients: new Set,
|
|
308
|
+
disconnect() {
|
|
309
|
+
// @ts-expect-error todo: update selection dispatch types
|
|
310
|
+
selection.removeEventListener('activate', activate);
|
|
311
|
+
selection.removeEventListener('value', value);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
mc.filterGroups?.set(selection, entry);
|
|
315
|
+
}
|
|
316
|
+
entry.clients.add(client);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Activate a selection, providing a clause indicative of potential
|
|
321
|
+
* next updates. Activation provides a preview of likely next events,
|
|
322
|
+
* enabling potential precomputation to optimize updates.
|
|
323
|
+
* @param mc The Mosaic coordinator.
|
|
324
|
+
* @param selection A selection.
|
|
325
|
+
* @param clause A selection clause for the activation.
|
|
326
|
+
*/
|
|
327
|
+
function activateSelection(
|
|
328
|
+
mc: Coordinator,
|
|
329
|
+
selection: Selection,
|
|
330
|
+
clause: SelectionClause
|
|
331
|
+
): void {
|
|
332
|
+
const { preaggregator, filterGroups } = mc;
|
|
333
|
+
const { clients } = filterGroups.get(selection)!;
|
|
334
|
+
for (const client of clients) {
|
|
335
|
+
if (client.enabled) {
|
|
336
|
+
preaggregator.request(client, selection, clause);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Process an updated selection value, querying filtered data for any
|
|
343
|
+
* associated clients.
|
|
344
|
+
* @param mc The Mosaic coordinator.
|
|
345
|
+
* @param selection A selection.
|
|
346
|
+
* @returns A Promise that resolves when the update completes.
|
|
347
|
+
*/
|
|
348
|
+
function updateSelection(
|
|
349
|
+
mc: Coordinator,
|
|
350
|
+
selection: Selection
|
|
351
|
+
): Promise<PromiseSettledResult<unknown>[]> {
|
|
352
|
+
const { preaggregator, filterGroups } = mc;
|
|
353
|
+
const { clients } = filterGroups!.get(selection)!;
|
|
354
|
+
const { active } = selection;
|
|
355
|
+
return Promise.allSettled(Array.from(clients, (client: MosaicClient) => {
|
|
356
|
+
if (!client.enabled) return client.requestQuery();
|
|
357
|
+
const info = preaggregator.request(client, selection, active);
|
|
358
|
+
const filter = info ? null : selection.predicate(client);
|
|
359
|
+
|
|
360
|
+
// skip due to cross-filtering
|
|
361
|
+
if (info?.skip || (!info && !filter)) return;
|
|
362
|
+
|
|
363
|
+
// @ts-expect-error FIXME
|
|
364
|
+
const query = info?.query(active.predicate) ?? client.query(filter);
|
|
365
|
+
return mc.updateClient(client, query);
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { FilterExpr, type Query } from '@uwdata/mosaic-sql';
|
|
2
|
+
import { type Coordinator } from './Coordinator.js';
|
|
3
|
+
import { type Selection } from './Selection.js';
|
|
4
4
|
import { throttle } from './util/throttle.js';
|
|
5
5
|
|
|
6
|
+
export type ClientQuery = Query | string | null;
|
|
7
|
+
|
|
8
|
+
export function isMosaicClient(x: unknown): x is MosaicClient {
|
|
9
|
+
return x instanceof MosaicClient;
|
|
10
|
+
}
|
|
11
|
+
|
|
6
12
|
/**
|
|
7
13
|
* A Mosaic client is a data consumer that indicates its data needs to a
|
|
8
14
|
* Mosaic coordinator via the query method. The coordinator is responsible
|
|
@@ -20,53 +26,55 @@ import { throttle } from './util/throttle.js';
|
|
|
20
26
|
* associated interface elements are offscreen or disabled.
|
|
21
27
|
*/
|
|
22
28
|
export class MosaicClient {
|
|
29
|
+
_filterBy: Selection | undefined;
|
|
30
|
+
_requestUpdate: () => void;
|
|
31
|
+
_coordinator: Coordinator | null;
|
|
32
|
+
_pending: Promise<unknown>;
|
|
33
|
+
_enabled: boolean;
|
|
34
|
+
_initialized: boolean;
|
|
35
|
+
_request: Query | boolean | null;
|
|
36
|
+
|
|
23
37
|
/**
|
|
24
38
|
* Create a new client instance.
|
|
25
|
-
* @param
|
|
39
|
+
* @param filterSelection An optional selection to
|
|
26
40
|
* interactively filter this client's data. If provided, a coordinator
|
|
27
41
|
* will re-query and update the client when the selection updates.
|
|
28
42
|
*/
|
|
29
|
-
constructor(filterSelection) {
|
|
30
|
-
/** @type {Selection | undefined} */
|
|
43
|
+
constructor(filterSelection?: Selection) {
|
|
31
44
|
this._filterBy = filterSelection;
|
|
32
45
|
this._requestUpdate = throttle(() => this.requestQuery(), true);
|
|
33
|
-
/** @type {Coordinator | null} */
|
|
34
46
|
this._coordinator = null;
|
|
35
|
-
/** @type {Promise<any>} */
|
|
36
47
|
this._pending = Promise.resolve();
|
|
37
|
-
/** @type {boolean} */
|
|
38
48
|
this._enabled = true;
|
|
39
|
-
/** @type {boolean} */
|
|
40
49
|
this._initialized = false;
|
|
41
|
-
/** @type {Query | boolean | null} */
|
|
42
50
|
this._request = null;
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
/**
|
|
46
|
-
* @returns
|
|
54
|
+
* @returns this client's connected coordinator.
|
|
47
55
|
*/
|
|
48
|
-
get coordinator() {
|
|
56
|
+
get coordinator(): Coordinator | null {
|
|
49
57
|
return this._coordinator;
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
/**
|
|
53
61
|
* Set this client's connected coordinator.
|
|
54
62
|
*/
|
|
55
|
-
set coordinator(coordinator) {
|
|
63
|
+
set coordinator(coordinator: Coordinator | null) {
|
|
56
64
|
this._coordinator = coordinator;
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
/**
|
|
60
68
|
* Return this client's enabled state.
|
|
61
69
|
*/
|
|
62
|
-
get enabled() {
|
|
70
|
+
get enabled(): boolean {
|
|
63
71
|
return this._enabled;
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
/**
|
|
67
75
|
* Set this client's enabled state;
|
|
68
76
|
*/
|
|
69
|
-
set enabled(state) {
|
|
77
|
+
set enabled(state: boolean) {
|
|
70
78
|
state = !!state; // ensure boolean
|
|
71
79
|
if (this._enabled !== state) {
|
|
72
80
|
this._enabled = state;
|
|
@@ -86,14 +94,14 @@ export class MosaicClient {
|
|
|
86
94
|
/**
|
|
87
95
|
* Return a Promise that resolves once the client has updated.
|
|
88
96
|
*/
|
|
89
|
-
get pending() {
|
|
97
|
+
get pending(): Promise<unknown> {
|
|
90
98
|
return this._pending;
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
/**
|
|
94
|
-
* @returns
|
|
102
|
+
* @returns this client's filter selection.
|
|
95
103
|
*/
|
|
96
|
-
get filterBy() {
|
|
104
|
+
get filterBy(): Selection | undefined {
|
|
97
105
|
return this._filterBy;
|
|
98
106
|
}
|
|
99
107
|
|
|
@@ -103,7 +111,7 @@ export class MosaicClient {
|
|
|
103
111
|
* to the filterBy selection do not change the groupby domain of the client
|
|
104
112
|
* query.
|
|
105
113
|
*/
|
|
106
|
-
get filterStable() {
|
|
114
|
+
get filterStable(): boolean {
|
|
107
115
|
return true;
|
|
108
116
|
}
|
|
109
117
|
|
|
@@ -112,41 +120,41 @@ export class MosaicClient {
|
|
|
112
120
|
* should override this method as needed, potentially issuing one or more
|
|
113
121
|
* queries to gather data or metadata needed prior to `query` calls.
|
|
114
122
|
*/
|
|
115
|
-
async prepare() {
|
|
123
|
+
async prepare(): Promise<void> {
|
|
116
124
|
}
|
|
117
125
|
|
|
118
126
|
/**
|
|
119
127
|
* Return a query specifying the data needed by this client.
|
|
120
|
-
* @param
|
|
121
|
-
* @returns
|
|
128
|
+
* @param filter The filtering criteria to apply in the query.
|
|
129
|
+
* @returns The client query
|
|
122
130
|
*/
|
|
123
|
-
query(filter) { // eslint-disable-line no-unused-vars
|
|
131
|
+
query(filter?: FilterExpr | null): ClientQuery { // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
124
132
|
return null;
|
|
125
133
|
}
|
|
126
134
|
|
|
127
135
|
/**
|
|
128
136
|
* Called by the coordinator to inform the client that a query is pending.
|
|
129
|
-
* @returns
|
|
137
|
+
* @returns this
|
|
130
138
|
*/
|
|
131
|
-
queryPending() {
|
|
139
|
+
queryPending(): this {
|
|
132
140
|
return this;
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
/**
|
|
136
144
|
* Called by the coordinator to return a query result.
|
|
137
|
-
* @param
|
|
138
|
-
* @returns
|
|
145
|
+
* @param data The query result.
|
|
146
|
+
* @returns this
|
|
139
147
|
*/
|
|
140
|
-
queryResult(data) { // eslint-disable-line no-unused-vars
|
|
148
|
+
queryResult(data: unknown): this { // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
141
149
|
return this;
|
|
142
150
|
}
|
|
143
151
|
|
|
144
152
|
/**
|
|
145
153
|
* Called by the coordinator to report a query execution error.
|
|
146
|
-
* @param
|
|
147
|
-
* @returns
|
|
154
|
+
* @param error
|
|
155
|
+
* @returns this
|
|
148
156
|
*/
|
|
149
|
-
queryError(error) { // eslint-disable-line no-unused-vars
|
|
157
|
+
queryError(error: Error): this { // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
150
158
|
// do nothing, the coordinator logs the error
|
|
151
159
|
return this;
|
|
152
160
|
}
|
|
@@ -158,15 +166,14 @@ export class MosaicClient {
|
|
|
158
166
|
* no effect if the client is not connected to a coordinator. If the client
|
|
159
167
|
* is connected by currently disabled, the request will be serviced if the
|
|
160
168
|
* client is later enabled.
|
|
161
|
-
* @param
|
|
162
|
-
* will be
|
|
169
|
+
* @param query The query to request. If unspecified, the query
|
|
170
|
+
* will be determined by the client's `query` method and the current
|
|
163
171
|
* `filterBy` selection state.
|
|
164
|
-
* @returns {Promise}
|
|
165
172
|
*/
|
|
166
|
-
requestQuery(query) {
|
|
173
|
+
requestQuery(query?: Query): Promise<unknown> | null {
|
|
167
174
|
if (this._enabled) {
|
|
168
175
|
const q = query || this.query(this.filterBy?.predicate(this));
|
|
169
|
-
return this._coordinator
|
|
176
|
+
return this._coordinator!.requestQuery(this, q);
|
|
170
177
|
} else {
|
|
171
178
|
this._request = query ?? true;
|
|
172
179
|
return null;
|
|
@@ -181,7 +188,7 @@ export class MosaicClient {
|
|
|
181
188
|
* connected to a coordinator. If the client is connected but currently
|
|
182
189
|
* disabled, the request will be serviced if the client is later enabled.
|
|
183
190
|
*/
|
|
184
|
-
requestUpdate() {
|
|
191
|
+
requestUpdate(): void {
|
|
185
192
|
if (this._enabled) {
|
|
186
193
|
this._requestUpdate();
|
|
187
194
|
} else {
|
|
@@ -193,7 +200,7 @@ export class MosaicClient {
|
|
|
193
200
|
* Reset this client, calling the prepare method and query requests. This
|
|
194
201
|
* method has no effect if the client is not registered with a coordinator.
|
|
195
202
|
*/
|
|
196
|
-
initialize() {
|
|
203
|
+
initialize(): void {
|
|
197
204
|
if (!this._enabled) {
|
|
198
205
|
// clear flag so we initialize when enabled again
|
|
199
206
|
this._initialized = false;
|
|
@@ -212,16 +219,15 @@ export class MosaicClient {
|
|
|
212
219
|
* If overriding this method in a client subclass, be sure to also
|
|
213
220
|
* disconnect from the coordinator.
|
|
214
221
|
*/
|
|
215
|
-
destroy() {
|
|
222
|
+
destroy(): void {
|
|
216
223
|
this.coordinator?.disconnect(this);
|
|
217
224
|
}
|
|
218
225
|
|
|
219
226
|
/**
|
|
220
227
|
* Requests a client update, for example to (re-)render an interface
|
|
221
228
|
* component.
|
|
222
|
-
* @returns {this | Promise<any>}
|
|
223
229
|
*/
|
|
224
|
-
update() {
|
|
230
|
+
update(): this | Promise<unknown> {
|
|
225
231
|
return this;
|
|
226
232
|
}
|
|
227
|
-
}
|
|
233
|
+
}
|