@uwdata/mosaic-core 0.15.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/types/Coordinator.d.ts +22 -25
  2. package/dist/types/MosaicClient.d.ts +20 -11
  3. package/dist/types/QueryManager.d.ts +42 -17
  4. package/dist/types/Selection.d.ts +1 -1
  5. package/dist/types/SelectionClause.d.ts +1 -1
  6. package/dist/types/connectors/Connector.d.ts +25 -0
  7. package/dist/types/connectors/rest.d.ts +13 -17
  8. package/dist/types/connectors/socket.d.ts +98 -16
  9. package/dist/types/connectors/wasm.d.ts +133 -14
  10. package/dist/types/index-types.d.ts +1 -0
  11. package/dist/types/preagg/PreAggregator.d.ts +1 -1
  12. package/dist/types/preagg/preagg-columns.d.ts +2 -2
  13. package/dist/types/preagg/sufficient-statistics.d.ts +2 -2
  14. package/dist/types/types.d.ts +21 -0
  15. package/dist/types/util/cache.d.ts +14 -10
  16. package/dist/types/util/decode-ipc.d.ts +9 -4
  17. package/dist/types/util/void-logger.d.ts +3 -0
  18. package/package.json +4 -4
  19. package/src/Coordinator.js +15 -12
  20. package/src/MosaicClient.js +17 -5
  21. package/src/QueryConsolidator.js +2 -1
  22. package/src/QueryManager.js +31 -0
  23. package/src/Selection.js +4 -2
  24. package/src/SelectionClause.js +5 -2
  25. package/src/connectors/Connector.ts +30 -0
  26. package/src/connectors/rest.js +14 -12
  27. package/src/connectors/socket.js +112 -77
  28. package/src/connectors/wasm.js +118 -55
  29. package/src/index-types.ts +1 -0
  30. package/src/make-client.js +0 -4
  31. package/src/preagg/PreAggregator.js +8 -6
  32. package/src/preagg/preagg-columns.js +5 -2
  33. package/src/preagg/sufficient-statistics.js +2 -1
  34. package/src/types.ts +23 -0
  35. package/src/util/cache.js +20 -5
  36. package/src/util/decode-ipc.js +9 -5
  37. package/src/util/field-info.js +2 -1
  38. package/src/util/js-type.js +3 -1
  39. package/src/util/void-logger.js +4 -1
@@ -1,81 +1,144 @@
1
+ /** @import { ExtractionOptions, Table } from '@uwdata/flechette' */
2
+ /** @import { ArrowQueryRequest, Connector, ExecQueryRequest, JSONQueryRequest } from './Connector.js' */
1
3
  import * as duckdb from '@duckdb/duckdb-wasm';
2
4
  import { decodeIPC } from '../util/decode-ipc.js';
3
5
 
4
- // bypass duckdb-wasm query method to get Arrow IPC bytes directly
5
- // https://github.com/duckdb/duckdb-wasm/issues/267#issuecomment-2252749509
6
- function getArrowIPC(con, query) {
7
- return new Promise((resolve, reject) => {
8
- con.useUnsafe(async (bindings, conn) => {
9
- try {
10
- const buffer = await bindings.runQuery(conn, query);
11
- resolve(buffer);
12
- } catch (error) {
13
- reject(error);
14
- }
15
- });
16
- });
17
- }
6
+ /**
7
+ * @typedef {object} DuckDBWASMOptions
8
+ * @property {boolean} [log] Flag to enable logging.
9
+ */
10
+
11
+ /**
12
+ * @typedef {object} DuckDBWASMConnectorOptions
13
+ * @property {boolean} [log] Flag to enable logging.
14
+ * @property {ExtractionOptions} [ipc]
15
+ * Arrow IPC extraction options.
16
+ * @property {duckdb.AsyncDuckDB} [duckdb]
17
+ * Optional pre-existing DuckDB-WASM instance.
18
+ * @property {duckdb.AsyncDuckDBConnection} [connection]
19
+ * Optional pre-existing DuckDB-WASM connection.
20
+ */
18
21
 
22
+ /**
23
+ * Connect to a DuckDB-WASM instance.
24
+ * @param {DuckDBWASMConnectorOptions} [options] Connector options.
25
+ * @returns {DuckDBWASMConnector} A connector instance.
26
+ */
19
27
  export function wasmConnector(options = {}) {
20
- const { duckdb, connection, ...opts } = options;
21
- let db = duckdb;
22
- let con = connection;
23
- let loadPromise;
28
+ return new DuckDBWASMConnector(options);
29
+ }
24
30
 
25
- function load() {
26
- if (!loadPromise) {
27
- // use a loading promise to avoid race conditions
28
- // synchronizes multiple callees on the same load
29
- loadPromise = (db
30
- ? Promise.resolve(db)
31
- : initDatabase(opts).then(result => db = result))
32
- .then(db => db.connect())
33
- .then(result => con = result);
34
- }
35
- return loadPromise;
31
+ /**
32
+ * DuckDB-WASM connector.
33
+ * @implements {Connector}
34
+ */
35
+ export class DuckDBWASMConnector {
36
+ /**
37
+ * Create a new DuckDB-WASM connector instance.
38
+ * @param {DuckDBWASMConnectorOptions} [options]
39
+ */
40
+ constructor(options = {}) {
41
+ const { ipc, duckdb, connection, ...opts } = options;
42
+ /** @type {ExtractionOptions} */
43
+ this._ipc = ipc;
44
+ /** @type {DuckDBWASMOptions} */
45
+ this._options = opts;
46
+ /** @type {duckdb.AsyncDuckDB} */
47
+ this._db = duckdb;
48
+ /** @type {duckdb.AsyncDuckDBConnection} */
49
+ this._con = connection;
50
+ /** @type {Promise<unknown>} */
51
+ this._loadPromise;
36
52
  }
37
53
 
38
54
  /**
39
55
  * Get the backing DuckDB-WASM instance.
40
- * Will lazily initialize DuckDB-WASM if not already loaded.
56
+ * Lazily initializes DuckDB-WASM if not already loaded.
41
57
  * @returns {Promise<duckdb.AsyncDuckDB>} The DuckDB-WASM instance.
42
58
  */
43
- async function getDuckDB() {
44
- if (!db) await load();
45
- return db;
59
+ async getDuckDB() {
60
+ if (!this._db) await connect(this);
61
+ return this._db;
46
62
  }
47
63
 
48
64
  /**
49
65
  * Get the backing DuckDB-WASM connection.
50
- * Will lazily initialize DuckDB-WASM if not already loaded.
66
+ * Lazily initializes DuckDB-WASM if not already loaded.
51
67
  * @returns {Promise<duckdb.AsyncDuckDBConnection>} The DuckDB-WASM connection.
52
68
  */
53
- async function getConnection() {
54
- if (!con) await load();
55
- return con;
69
+ async getConnection() {
70
+ if (!this._con) await connect(this);
71
+ return this._con;
56
72
  }
57
73
 
58
- return {
59
- getDuckDB,
60
- getConnection,
61
- /**
62
- * Query the DuckDB-WASM instance.
63
- * @param {object} query
64
- * @param {'exec' | 'arrow' | 'json'} [query.type] The query type.
65
- * @param {string} query.sql A SQL query string.
66
- * @returns the query result
67
- */
68
- query: async query => {
69
- const { type, sql } = query;
70
- const con = await getConnection();
71
- const result = await getArrowIPC(con, sql);
72
- return type === 'exec' ? undefined
73
- : type === 'arrow' ? decodeIPC(result)
74
- : decodeIPC(result).toArray();
75
- }
74
+ /**
75
+ * @overload
76
+ * @param {ArrowQueryRequest} query
77
+ * @returns {Promise<Table>}
78
+ *
79
+ * @overload
80
+ * @param {ExecQueryRequest} query
81
+ * @returns {Promise<void>}
82
+ *
83
+ * @overload
84
+ * @param {JSONQueryRequest} query
85
+ * @returns {Promise<Record<string, any>[]>}
86
+ *
87
+ * @param {ArrowQueryRequest | ExecQueryRequest | JSONQueryRequest} query
88
+ * @returns {Promise<Table | void | Record<string, any>[]>}}
89
+ */
90
+ async query(query) {
91
+ const { type, sql } = query;
92
+ const con = await this.getConnection();
93
+ const result = await getArrowIPC(con, sql);
94
+ return type === 'exec' ? undefined
95
+ : type === 'arrow' ? decodeIPC(result, this._ipc)
96
+ : decodeIPC(result).toArray();
76
97
  };
77
98
  }
78
99
 
100
+ /**
101
+ * Bypass duckdb-wasm query method to get Arrow IPC bytes directly.
102
+ * https://github.com/duckdb/duckdb-wasm/issues/267#issuecomment-2252749509
103
+ * @param {duckdb.AsyncDuckDBConnection} con The DuckDB-WASM connection.
104
+ * @param {string} query The SQL query to run.
105
+ */
106
+ function getArrowIPC(con, query) {
107
+ return new Promise((resolve, reject) => {
108
+ con.useUnsafe(async (bindings, conn) => {
109
+ try {
110
+ const buffer = await bindings.runQuery(conn, query);
111
+ resolve(buffer);
112
+ } catch (error) {
113
+ reject(error);
114
+ }
115
+ });
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Establish a new database connection for the given connector.
121
+ * @param {DuckDBWASMConnector} c The connector.
122
+ * @returns {Promise<unknown>}
123
+ */
124
+ function connect(c) {
125
+ if (!c._loadPromise) {
126
+ // use a loading promise to avoid race conditions
127
+ // synchronizes multiple callees on the same load
128
+ c._loadPromise = (
129
+ c._db
130
+ ? Promise.resolve(c._db)
131
+ : initDatabase(c._options).then(result => c._db = result))
132
+ .then(db => db.connect())
133
+ .then(result => c._con = result);
134
+ }
135
+ return c._loadPromise;
136
+ }
137
+
138
+ /**
139
+ * Initialize a new DuckDB-WASM instance.
140
+ * @param {DuckDBWASMOptions} options
141
+ */
79
142
  async function initDatabase({
80
143
  log = false
81
144
  } = {}) {
@@ -1,3 +1,4 @@
1
1
  export * from './index.js';
2
2
  export * from './types.js';
3
+ export * from './connectors/Connector.js';
3
4
  export * from './util/selection-types.js';
@@ -98,8 +98,4 @@ class ProxyClient extends MosaicClient {
98
98
  this._methods.queryError?.(error);
99
99
  return this;
100
100
  }
101
-
102
- destroy() {
103
- this.coordinator.disconnect(this);
104
- }
105
101
  }
@@ -1,9 +1,11 @@
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';
1
+ /**
2
+ * @import { ExprNode, SelectQuery } from '@uwdata/mosaic-sql'
3
+ * @import { Coordinator } from '../Coordinator.js'
4
+ * @import { MosaicClient } from '../MosaicClient.js'
5
+ * @import { Selection } from '../Selection.js'
6
+ * @import { BinMethod, Scale, SelectionClause } from '../util/selection-types.js'
7
+ */
8
+ import { Query, and, asNode, ceil, collectColumns, createTable, float64, floor, isBetween, int32, mul, round, scaleTransform, sub, isSelectQuery, isAggregateExpression, ColumnNameRefNode } from '@uwdata/mosaic-sql';
7
9
  import { preaggColumns } from './preagg-columns.js';
8
10
  import { fnv_hash } from '../util/hash.js';
9
11
 
@@ -1,5 +1,8 @@
1
- import { AggregateNode, ExprNode, Query, SelectQuery, collectAggregates, isAggregateExpression, isSelectQuery, isTableRef, rewrite, sql } from '@uwdata/mosaic-sql';
2
- import { MosaicClient } from '../MosaicClient.js';
1
+ /**
2
+ * @import { AggregateNode, ExprNode, Query, SelectQuery } from '@uwdata/mosaic-sql'
3
+ * @import { MosaicClient } from '../MosaicClient.js'
4
+ */
5
+ import { collectAggregates, isAggregateExpression, isSelectQuery, isTableRef, rewrite, sql } from '@uwdata/mosaic-sql';
3
6
  import { sufficientStatistics } from './sufficient-statistics.js';
4
7
 
5
8
  /**
@@ -1,4 +1,5 @@
1
- import { AggregateNode, and, argmax, argmin, coalesce, count, div, exp, ExprNode, isNotNull, ln, max, min, mul, pow, regrAvgX, regrAvgY, regrCount, sql, sqrt, sub, sum } from '@uwdata/mosaic-sql';
1
+ /** @import { AggregateNode, ExprNode } from '@uwdata/mosaic-sql' */
2
+ import { and, argmax, argmin, coalesce, count, div, exp, isNotNull, ln, max, min, mul, pow, regrAvgX, regrAvgY, regrCount, sql, sqrt, sub, sum } from '@uwdata/mosaic-sql';
2
3
  import { fnv_hash } from '../util/hash.js';
3
4
 
4
5
  /**
package/src/types.ts CHANGED
@@ -62,3 +62,26 @@ export interface Activatable {
62
62
  */
63
63
  activate(): void;
64
64
  }
65
+
66
+ /**
67
+ * Interface for cache implementations.
68
+ */
69
+ export interface Cache {
70
+ get(key: string): any;
71
+ set(key: string, value: any): any;
72
+ clear(): void;
73
+ }
74
+
75
+ /**
76
+ * Interface for logger implementations
77
+ */
78
+ export interface Logger {
79
+ debug(...args: any[]): void;
80
+ info(...args: any[]): void;
81
+ log(...args: any[]): void;
82
+ warn(...args: any[]): void;
83
+ error(...args: any[]): void;
84
+ group(label?: any): void;
85
+ groupCollapsed(label?: any): void;
86
+ groupEnd(): void;
87
+ }
package/src/util/cache.js CHANGED
@@ -1,13 +1,28 @@
1
+ /** @import { Cache } from '../types.js' */
2
+
1
3
  const requestIdle = typeof requestIdleCallback !== 'undefined'
2
4
  ? requestIdleCallback
3
5
  : setTimeout;
4
6
 
5
- export const voidCache = () => ({
6
- get: () => undefined,
7
- set: (key, value) => value,
8
- clear: () => {}
9
- });
7
+ /**
8
+ * Create a new cache that ignores all values.
9
+ * @returns {Cache}
10
+ */
11
+ export function voidCache() {
12
+ return {
13
+ get: () => undefined,
14
+ set: (key, value) => value,
15
+ clear: () => {}
16
+ };
17
+ }
10
18
 
19
+ /**
20
+ * Create a new cache that uses an LRU eviction policy.
21
+ * @param {object} [options] Cache options.
22
+ * @param {number} [options.max] Maximum number of cache entries.
23
+ * @param {number} [options.ttl] Time-to-live for cache entries.
24
+ * @returns {Cache}
25
+ */
11
26
  export function lruCache({
12
27
  max = 1000, // max entries
13
28
  ttl = 3 * 60 * 60 * 1000 // time-to-live, default 3 hours
@@ -1,11 +1,15 @@
1
+ /** @import { ExtractionOptions, Table } from '@uwdata/flechette' */
1
2
  import { tableFromIPC } from '@uwdata/flechette';
2
3
 
3
4
  /**
4
- * Decode Arrow IPC bytes to a table instance, with an option to map date and
5
- * timestamp values to JS Date objects.
5
+ * Decode Arrow IPC bytes to a table instance.
6
+ * The default options map date and timestamp values to JS Date objects.
6
7
  * @param {ArrayBuffer | Uint8Array} data Arrow IPC bytes.
7
- * @returns {import('@uwdata/flechette').Table} A table instance.
8
+ * @param {ExtractionOptions} [options] Arrow IPC extraction options.
9
+ * If unspecified, the default options will extract date and timestamp
10
+ * values to JS Date objects.
11
+ * @returns {Table} A table instance.
8
12
  */
9
- export function decodeIPC(data) {
10
- return tableFromIPC(data, { useDate: true });
13
+ export function decodeIPC(data, options = { useDate: true }) {
14
+ return tableFromIPC(data, options);
11
15
  }
@@ -1,4 +1,5 @@
1
- import { AggregateNode, Query, asTableRef, count, isAggregateExpression, isNode, isNull, max, min, sql } from '@uwdata/mosaic-sql';
1
+ /** @import { AggregateNode } from '@uwdata/mosaic-sql' */
2
+ import { Query, asTableRef, count, isAggregateExpression, isNode, isNull, max, min, sql } from '@uwdata/mosaic-sql';
2
3
  import { jsType } from './js-type.js';
3
4
 
4
5
  export const Count = 'count';
@@ -41,7 +41,9 @@ export function jsType(type) {
41
41
  case 'GEOMETRY':
42
42
  return 'object';
43
43
  default:
44
- if (type.startsWith('DECIMAL')) {
44
+ if (type.startsWith('ENUM')) {
45
+ return 'string';
46
+ } else if (type.startsWith('DECIMAL')) {
45
47
  return 'number';
46
48
  } else if (type.startsWith('STRUCT') || type.startsWith('MAP')) {
47
49
  return 'object';
@@ -5,6 +5,9 @@ export function voidLogger() {
5
5
  info(..._) {},
6
6
  log(..._) {},
7
7
  warn(..._) {},
8
- error(..._) {}
8
+ error(..._) {},
9
+ group(label) {},
10
+ groupCollapsed(label) {},
11
+ groupEnd() {}
9
12
  };
10
13
  }