@uwdata/mosaic-core 0.18.0 → 0.19.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 (43) hide show
  1. package/dist/src/Coordinator.d.ts +16 -4
  2. package/dist/src/Coordinator.d.ts.map +1 -1
  3. package/dist/src/Coordinator.js +0 -19
  4. package/dist/src/Coordinator.js.map +1 -1
  5. package/dist/src/MosaicClient.d.ts.map +1 -1
  6. package/dist/src/MosaicClient.js +1 -0
  7. package/dist/src/MosaicClient.js.map +1 -1
  8. package/dist/src/QueryConsolidator.js +4 -4
  9. package/dist/src/QueryConsolidator.js.map +1 -1
  10. package/dist/src/Selection.d.ts +9 -9
  11. package/dist/src/Selection.d.ts.map +1 -1
  12. package/dist/src/Selection.js +9 -6
  13. package/dist/src/Selection.js.map +1 -1
  14. package/dist/src/SelectionClause.d.ts +55 -25
  15. package/dist/src/SelectionClause.d.ts.map +1 -1
  16. package/dist/src/SelectionClause.js +52 -10
  17. package/dist/src/SelectionClause.js.map +1 -1
  18. package/dist/src/connectors/wasm.d.ts +3 -0
  19. package/dist/src/connectors/wasm.d.ts.map +1 -1
  20. package/dist/src/connectors/wasm.js +5 -2
  21. package/dist/src/connectors/wasm.js.map +1 -1
  22. package/dist/src/index.d.ts +1 -1
  23. package/dist/src/index.d.ts.map +1 -1
  24. package/dist/src/index.js +1 -1
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/make-client.d.ts +2 -4
  27. package/dist/src/make-client.d.ts.map +1 -1
  28. package/dist/src/make-client.js +1 -1
  29. package/dist/src/util/decode-ipc.d.ts +1 -1
  30. package/dist/src/util/decode-ipc.d.ts.map +1 -1
  31. package/dist/src/util/decode-ipc.js.map +1 -1
  32. package/dist/src/util/field-info.js.map +1 -1
  33. package/package.json +6 -6
  34. package/src/Coordinator.ts +32 -3
  35. package/src/MosaicClient.ts +1 -0
  36. package/src/QueryConsolidator.ts +4 -4
  37. package/src/Selection.ts +12 -9
  38. package/src/SelectionClause.ts +147 -65
  39. package/src/connectors/wasm.ts +9 -4
  40. package/src/index.ts +3 -1
  41. package/src/make-client.ts +2 -2
  42. package/src/util/decode-ipc.ts +4 -1
  43. package/src/util/field-info.ts +3 -3
@@ -1,6 +1,5 @@
1
-
2
1
  import { isMosaicClient, MosaicClient } from './MosaicClient.js';
3
- import { type ExprNode, type ExprValue, type ScaleOptions, type ScaleDomain, and, contains, isBetween, isIn, isNotDistinct, literal, or, prefix, regexp_matches, suffix } from '@uwdata/mosaic-sql';
2
+ import { type ExprNode, type ExprValue, type ScaleOptions, type ScaleDomain, and, contains, isBetween, isIn, isNotDistinct, literal, or, prefix, regexp_matches, suffix, listHasAny, listHasAll, lower } from '@uwdata/mosaic-sql';
4
3
 
5
4
  /**
6
5
  * Selection clause metadata to guide possible query optimizations.
@@ -26,7 +25,7 @@ export interface PointMetadata extends ClauseMetadata {
26
25
  * Selection clause metadata indicating text search matching.
27
26
  */
28
27
  export interface MatchMetadata extends ClauseMetadata {
29
- type: MatchMethod;
28
+ type: 'match';
30
29
  /** The text search matching method used. */
31
30
  method?: 'contains' | 'prefix' | 'suffix' | 'regexp' | (string & {});
32
31
  }
@@ -59,9 +58,7 @@ export interface IntervalMetadata extends ClauseMetadata {
59
58
  bin?: BinMethod
60
59
  }
61
60
 
62
- export interface ClauseSource {
63
- reset?: () => void;
64
- }
61
+ export type ClauseSource = object & { reset?: () => void; };
65
62
 
66
63
  /**
67
64
  * A selection clause representing filtering criteria
@@ -98,6 +95,11 @@ export interface SelectionClause {
98
95
  meta?: ClauseMetadata;
99
96
  }
100
97
 
98
+ interface PointOptions {
99
+ source: ClauseSource;
100
+ clients?: Set<MosaicClient>;
101
+ }
102
+
101
103
  /**
102
104
  * Generate a selection clause for a single selected point value.
103
105
  * @param field The table column or expression to select.
@@ -109,13 +111,13 @@ export interface SelectionClause {
109
111
  * cross-filtering contexts.
110
112
  * @returns The generated selection clause.
111
113
  */
112
- export function clausePoint(field: ExprValue, value: unknown, {
113
- source,
114
- clients = isMosaicClient(source) ? new Set([source]) : undefined
115
- }: {
116
- source: ClauseSource;
117
- clients?: Set<MosaicClient>;
118
- }): SelectionClause {
114
+ export function clausePoint(
115
+ field: ExprValue,
116
+ value: unknown, {
117
+ source,
118
+ clients = isMosaicClient(source) ? new Set([source]) : undefined
119
+ }: PointOptions
120
+ ): SelectionClause {
119
121
  const predicate: ExprNode | null = value !== undefined
120
122
  ? isIn(field, [literal(value)])
121
123
  : null;
@@ -140,15 +142,16 @@ export function clausePoint(field: ExprValue, value: unknown, {
140
142
  * cross-filtering contexts.
141
143
  * @returns The generated selection clause.
142
144
  */
143
- export function clausePoints(fields: ExprValue[], value: unknown[][] | null | undefined, {
144
- source,
145
- clients = isMosaicClient(source) ? new Set([source]) : undefined
146
- }: {
147
- source: ClauseSource;
148
- clients?: Set<MosaicClient>;
149
- }): SelectionClause {
145
+ export function clausePoints(
146
+ fields: ExprValue[],
147
+ value: unknown[][] | null | undefined,
148
+ {
149
+ source,
150
+ clients = isMosaicClient(source) ? new Set([source]) : undefined
151
+ }: PointOptions
152
+ ): SelectionClause {
150
153
  let predicate: ExprNode | null = null;
151
- if (value) {
154
+ if (value?.length) {
152
155
  const clauses = value.length && fields.length === 1
153
156
  ? [isIn(fields[0], value.map(v => literal(v[0])))]
154
157
  : value.map(v => and(v.map((_, i) => isNotDistinct(fields[i], literal(_)))));
@@ -165,6 +168,14 @@ export function clausePoints(fields: ExprValue[], value: unknown[][] | null | un
165
168
  };
166
169
  }
167
170
 
171
+ /** Interval selection clause options. */
172
+ interface IntervalOptions {
173
+ source: ClauseSource;
174
+ clients?: Set<MosaicClient>;
175
+ bin?: BinMethod;
176
+ pixelSize?: number;
177
+ }
178
+
168
179
  /**
169
180
  * Generate a selection clause for a selected 1D interval.
170
181
  * @param field The table column or expression to select.
@@ -179,19 +190,17 @@ export function clausePoints(fields: ExprValue[], value: unknown[][] | null | un
179
190
  * @param options.pixelSize The interactive pixel size.
180
191
  * @returns The generated selection clause.
181
192
  */
182
- export function clauseInterval(field: ExprValue, value: ScaleDomain | null | undefined, {
183
- source,
184
- clients = isMosaicClient(source) ? new Set([source]) : undefined,
185
- bin,
186
- scale,
187
- pixelSize = 1
188
- }: {
189
- source: ClauseSource;
190
- clients?: Set<MosaicClient>;
191
- scale?: ScaleOptions;
192
- bin?: BinMethod;
193
- pixelSize?: number;
194
- }): SelectionClause {
193
+ export function clauseInterval(
194
+ field: ExprValue,
195
+ value: ScaleDomain | null | undefined,
196
+ {
197
+ source,
198
+ clients = isMosaicClient(source) ? new Set([source]) : undefined,
199
+ bin,
200
+ scale,
201
+ pixelSize = 1
202
+ }: IntervalOptions & { scale?: ScaleOptions }
203
+ ): SelectionClause {
195
204
  const predicate = value != null ? isBetween(field, value) : null;
196
205
  const meta: IntervalMetadata = {
197
206
  type: 'interval',
@@ -217,19 +226,17 @@ export function clauseInterval(field: ExprValue, value: ScaleDomain | null | und
217
226
  * @param options.pixelSize The interactive pixel size.
218
227
  * @returns The generated selection clause.
219
228
  */
220
- export function clauseIntervals(fields: ExprValue[], value: ScaleDomain[] | null | undefined, {
221
- source,
222
- clients = isMosaicClient(source) ? new Set([source]) : undefined,
223
- bin,
224
- scales = [],
225
- pixelSize = 1
226
- }: {
227
- source: ClauseSource;
228
- clients?: Set<MosaicClient>;
229
- scales?: ScaleOptions[];
230
- bin?: BinMethod;
231
- pixelSize?: number;
232
- }): SelectionClause {
229
+ export function clauseIntervals(
230
+ fields: ExprValue[],
231
+ value: ScaleDomain[] | null | undefined,
232
+ {
233
+ source,
234
+ clients = isMosaicClient(source) ? new Set([source]) : undefined,
235
+ bin,
236
+ scales = [],
237
+ pixelSize = 1
238
+ }: IntervalOptions & { scales?: ScaleOptions[] }
239
+ ): SelectionClause {
233
240
  const predicate = value != null
234
241
  ? and(fields.map((f, i) => isBetween(f, value[i])))
235
242
  : null;
@@ -242,34 +249,109 @@ export function clauseIntervals(fields: ExprValue[], value: ScaleDomain[] | null
242
249
  return { meta, source, clients, value, predicate };
243
250
  }
244
251
 
252
+ const identity = (x: string | ExprNode) => x;
253
+
245
254
  const MATCH_METHODS = { contains, prefix, suffix, regexp: regexp_matches };
246
255
 
247
256
  /** Text search matching methods. */
248
- export type MatchMethod = keyof typeof MATCH_METHODS | (string & {});
257
+ export type MatchMethod = keyof typeof MATCH_METHODS;
258
+
259
+ /** Text matching selection clause options. */
260
+ export interface MatchOptions {
261
+ source: ClauseSource;
262
+ clients?: Set<MosaicClient>;
263
+ method?: MatchMethod;
264
+ caseSensitive?: boolean;
265
+ }
249
266
 
250
267
  /**
251
- * Generate a selection clause for text search matching.
252
- * @param field The table column or expression to select.
268
+ * Generate a selection clause for text search matching over a single column.
269
+ * @param field The table column or expression to match.
253
270
  * @param value The selected text search query string.
254
271
  * @param options Additional clause properties.
255
272
  * @param options.source The source component generating this clause.
256
- * @param options.clients The Mosaic clients associated
257
- * with this clause. These clients are not filtered by this clause in
258
- * cross-filtering contexts.
259
- * @param options.method The text matching method to use. Defaults to `'contains'`.
273
+ * @param options.clients Mosaic clients associated with this clause.
274
+ * These clients are not filtered by this clause in cross-filtering contexts.
275
+ * @param options.method The text matching method to use, default `'contains'`.
276
+ * @param options.caseSensitive Flag for case sensitive matching, default `false`.
260
277
  * @returns The generated selection clause.
261
278
  */
262
- export function clauseMatch(field: ExprValue, value: string | null | undefined, {
263
- source,
264
- clients = undefined,
265
- method = 'contains'
266
- }: {
267
- source: ClauseSource;
268
- clients?: Set<MosaicClient>;
269
- method?: MatchMethod;
270
- }): SelectionClause {
279
+ export function clauseMatch(
280
+ field: string | ExprNode,
281
+ value: string | null | undefined,
282
+ {
283
+ source,
284
+ clients = undefined,
285
+ method = 'contains',
286
+ caseSensitive = false
287
+ }: MatchOptions
288
+ ): SelectionClause {
271
289
  const fn = MATCH_METHODS[method as keyof typeof MATCH_METHODS];
272
- const predicate: ExprNode | null = value ? fn(field, literal(value)) : null;
290
+ const transform = caseSensitive ? identity: lower;
291
+ const predicate = value ? fn(transform(field), transform(literal(value))) : null;
273
292
  const meta: MatchMetadata = { type: 'match', method };
274
293
  return { meta, source, clients, value, predicate };
275
- }
294
+ }
295
+
296
+ /**
297
+ * Generate a selection clause for text search matching over multiple columns.
298
+ * A match will succeed if any field successfully matches.
299
+ * @param fields The table columns or expressions to match.
300
+ * @param value The selected text search query string.
301
+ * @param options Additional clause properties.
302
+ * @param options.source The source component generating this clause.
303
+ * @param options.clients Mosaic clients associated with this clause.
304
+ * These clients are not filtered by this clause in cross-filtering contexts.
305
+ * @param options.method The text matching method to use, default `'contains'`.
306
+ * @param options.caseSensitive Flag for case sensitive matching, default `false`.
307
+ * @returns The generated selection clause.
308
+ */
309
+ export function clauseMatchAny(
310
+ fields: (string | ExprNode)[],
311
+ value: string | null,
312
+ {
313
+ source,
314
+ clients = undefined,
315
+ method = 'contains',
316
+ caseSensitive = false
317
+ }: MatchOptions
318
+ ): SelectionClause {
319
+ value = value || null;
320
+ const fn = MATCH_METHODS[method];
321
+ const transform = caseSensitive ? identity : lower;
322
+ const query = transform(literal(value));
323
+ const predicate = value
324
+ ? or(fields.flatMap(field => value ? fn(transform(field), query) : []))
325
+ : null;
326
+ const meta: MatchMetadata = { type: 'match', method };
327
+ return { meta, source, clients, value, predicate };
328
+ }
329
+
330
+ /**
331
+ * Generate a selection clause for a single selected point value in a list.
332
+ * @param field The table column or expression to select, which must be a list.
333
+ * @param value The selected value.
334
+ * @param options Additional clause properties.
335
+ * @param options.source The source component generating this clause.
336
+ * @param options.clients The Mosaic clients associated
337
+ * with this clause. These clients are not filtered by this clause in
338
+ * cross-filtering contexts.
339
+ * @returns The generated selection clause.
340
+ */
341
+ export function clauseList(
342
+ field: ExprValue,
343
+ value: unknown,
344
+ {
345
+ source,
346
+ clients = isMosaicClient(source) ? new Set([source]) : undefined,
347
+ listMatch = 'any'
348
+ }: {
349
+ source: ClauseSource;
350
+ clients?: Set<MosaicClient>;
351
+ listMatch?: 'any' | 'all';
352
+ }
353
+ ): SelectionClause {
354
+ const listFn = listMatch === 'all' ? listHasAll : listHasAny;
355
+ const predicate = value !== undefined ? listFn(field, literal(value)) : null;
356
+ return { source, clients, value, predicate };
357
+ }
@@ -15,6 +15,8 @@ interface DuckDBWASMConnectorOptions extends DuckDBWASMOptions {
15
15
  duckdb?: duckdb.AsyncDuckDB;
16
16
  /** Optional pre-existing DuckDB-WASM connection. */
17
17
  connection?: duckdb.AsyncDuckDBConnection;
18
+ /** Optional database config. */
19
+ config?: duckdb.DuckDBConfig;
18
20
  }
19
21
 
20
22
  /**
@@ -34,6 +36,7 @@ export class DuckDBWASMConnector implements Connector {
34
36
  public _options: DuckDBWASMOptions;
35
37
  public _db?: duckdb.AsyncDuckDB;
36
38
  public _con?: duckdb.AsyncDuckDBConnection;
39
+ public _config?: duckdb.DuckDBConfig;
37
40
  public _loadPromise?: Promise<unknown>;
38
41
 
39
42
  /**
@@ -41,11 +44,12 @@ export class DuckDBWASMConnector implements Connector {
41
44
  * @param options Connector options.
42
45
  */
43
46
  constructor(options: DuckDBWASMConnectorOptions = {}) {
44
- const { ipc, duckdb, connection, ...opts } = options;
47
+ const { ipc, duckdb, connection, config, ...opts } = options;
45
48
  this._ipc = ipc;
46
49
  this._options = opts;
47
50
  this._db = duckdb;
48
51
  this._con = connection;
52
+ this._config = config;
49
53
  }
50
54
 
51
55
  /**
@@ -87,7 +91,7 @@ export class DuckDBWASMConnector implements Connector {
87
91
  * @param con The DuckDB-WASM connection.
88
92
  * @param query The SQL query to run.
89
93
  */
90
- function getArrowIPC(con: duckdb.AsyncDuckDBConnection, query: string): Promise<ArrayBuffer> {
94
+ function getArrowIPC(con: duckdb.AsyncDuckDBConnection, query: string): Promise<Uint8Array> {
91
95
  return new Promise((resolve, reject) => {
92
96
  con.useUnsafe(async (bindings, conn) => {
93
97
  try {
@@ -113,6 +117,7 @@ function connect(c: DuckDBWASMConnector): Promise<unknown> {
113
117
  c._db
114
118
  ? Promise.resolve(c._db)
115
119
  : initDatabase(c._options).then(result => c._db = result))
120
+ .then(db => c._config != undefined ? db.open(c._config).then(() => db) : db)
116
121
  .then(db => db.connect())
117
122
  .then(result => c._con = result);
118
123
  }
@@ -135,7 +140,7 @@ async function initDatabase({
135
140
  new Blob([`importScripts("${bundle.mainWorker}");`], {type: 'text/javascript'})
136
141
  );
137
142
 
138
- // Instantiate the asynchronus version of DuckDB-wasm
143
+ // Instantiate the asynchronous version of DuckDB-wasm
139
144
  const worker = new Worker(worker_url);
140
145
  const logger = log ? new duckdb.ConsoleLogger() : new duckdb.VoidLogger();
141
146
  const db = new duckdb.AsyncDuckDB(logger, worker);
@@ -143,4 +148,4 @@ async function initDatabase({
143
148
  URL.revokeObjectURL(worker_url);
144
149
 
145
150
  return db;
146
- }
151
+ }
package/src/index.ts CHANGED
@@ -17,9 +17,11 @@ export { DuckDBWASMConnector } from './connectors/wasm.js';
17
17
  export {
18
18
  clauseInterval,
19
19
  clauseIntervals,
20
+ clauseList,
20
21
  clausePoint,
21
22
  clausePoints,
22
- clauseMatch
23
+ clauseMatch,
24
+ clauseMatchAny
23
25
  } from './SelectionClause.js';
24
26
 
25
27
  export { decodeIPC } from './util/decode-ipc.js';
@@ -30,9 +30,9 @@ export interface MakeClientOptions {
30
30
  * Make a new client with the given options, and connect the client to the
31
31
  * provided coordinator.
32
32
  * @param options The options for making the client.
33
- * @returns The resulting client, along with a method to destroy the client when no longer needed.
33
+ * @returns The resulting client.
34
34
  */
35
- export function makeClient(options: MakeClientOptions): MosaicClient & { destroy: () => void } {
35
+ export function makeClient(options: MakeClientOptions): MosaicClient {
36
36
  const {
37
37
  coordinator = defaultCoordinator(),
38
38
  ...clientOptions
@@ -10,6 +10,9 @@ import { tableFromIPC } from '@uwdata/flechette';
10
10
  * values to JS Date objects.
11
11
  * @returns A table instance.
12
12
  */
13
- export function decodeIPC(data: ArrayBuffer | Uint8Array, options: ExtractionOptions = { useDate: true }): Table {
13
+ export function decodeIPC(
14
+ data: ArrayBufferLike | Uint8Array,
15
+ options: ExtractionOptions = { useDate: true }
16
+ ): Table {
14
17
  return tableFromIPC(data, options);
15
18
  }
@@ -66,7 +66,7 @@ async function getFieldInfo(mc: Coordinator, { table, column, stats }: FieldInfo
66
66
  .groupby(isNode(column) && isAggregateExpression(column) ? sql`ALL` : []);
67
67
 
68
68
  const [desc] = Array.from(
69
- await mc.query(Query.describe(q)) as Table
69
+ await mc.query(Query.describe(q))
70
70
  ) as ColumnDescription[];
71
71
  const info: FieldInfo = {
72
72
  table,
@@ -83,7 +83,7 @@ async function getFieldInfo(mc: Coordinator, { table, column, stats }: FieldInfo
83
83
  const [result] = await mc.query(
84
84
  summarize({ table, column, stats }),
85
85
  { persist: true }
86
- ) as Table;
86
+ );
87
87
 
88
88
  // extract summary stats, copy to field info, and return
89
89
  return Object.assign(info, result);
@@ -97,7 +97,7 @@ async function getFieldInfo(mc: Coordinator, { table, column, stats }: FieldInfo
97
97
  */
98
98
  async function getTableInfo(mc: Coordinator, table: string): Promise<FieldInfo[]> {
99
99
  const result = Array.from(
100
- await mc.query(`DESCRIBE ${asTableRef(table)}`) as Table
100
+ await mc.query(`DESCRIBE ${asTableRef(table)}`)
101
101
  ) as ColumnDescription[];
102
102
  return result.map(desc => ({
103
103
  table,