@uwdata/mosaic-core
Advanced tools
Comparing version 0.7.1 to 0.8.0
{ | ||
"name": "@uwdata/mosaic-core", | ||
"version": "0.7.1", | ||
"version": "0.8.0", | ||
"description": "Scalable and extensible linked data views.", | ||
@@ -26,3 +26,3 @@ "keywords": [ | ||
"build": "node ../../esbuild.js mosaic-core", | ||
"lint": "eslint src test --ext .js", | ||
"lint": "eslint src test", | ||
"test": "mocha 'test/**/*-test.js'", | ||
@@ -32,7 +32,10 @@ "prepublishOnly": "npm run test && npm run lint && npm run build" | ||
"dependencies": { | ||
"@duckdb/duckdb-wasm": "^1.28.1-dev109.0", | ||
"@uwdata/mosaic-sql": "^0.7.0", | ||
"apache-arrow": "^15.0.0" | ||
"@duckdb/duckdb-wasm": "^1.28.1-dev194.0", | ||
"@uwdata/mosaic-sql": "^0.8.0", | ||
"apache-arrow": "^15.0.2" | ||
}, | ||
"gitHead": "7e6f3ea9b3011ea2c9201c1aa16e8e5664621a4c" | ||
"devDependencies": { | ||
"@uwdata/mosaic-duckdb": "^0.8.0" | ||
}, | ||
"gitHead": "a24b4c9f7dfa1c38c6af96ec17e075326c1af9b0" | ||
} |
@@ -25,3 +25,3 @@ import * as duckdb from '@duckdb/duckdb-wasm'; | ||
* Will lazily initialize DuckDB-WASM if not already loaded. | ||
* @returns {duckdb.AsyncDuckDB} The DuckDB-WASM instance. | ||
* @returns {Promise<duckdb.AsyncDuckDB>} The DuckDB-WASM instance. | ||
*/ | ||
@@ -36,3 +36,3 @@ async function getDuckDB() { | ||
* Will lazily initialize DuckDB-WASM if not already loaded. | ||
* @returns {duckdb.AsyncDuckDBConnection} The DuckDB-WASM connection. | ||
* @returns {Promise<duckdb.AsyncDuckDBConnection>} The DuckDB-WASM connection. | ||
*/ | ||
@@ -39,0 +39,0 @@ async function getConnection() { |
@@ -11,4 +11,3 @@ import { socketConnector } from './connectors/socket.js'; | ||
* Set or retrieve the coordinator instance. | ||
* | ||
* @param {Coordinator} instance the coordinator instance to set | ||
* @param {Coordinator} [instance] the coordinator instance to set | ||
* @returns {Coordinator} the coordinator instance | ||
@@ -46,3 +45,11 @@ */ | ||
configure({ cache = true, consolidate = true, indexes = true }) { | ||
/** | ||
* Set configuration options for this coordinator. | ||
* @param {object} [options] Configration options. | ||
* @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching. | ||
* @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation. | ||
* @param {boolean|object} [options.indexes=true] Boolean flag to enable/disable | ||
* automatic data cube indexes or an index options object. | ||
*/ | ||
configure({ cache = true, consolidate = true, indexes = true } = {}) { | ||
this.manager.cache(cache); | ||
@@ -121,3 +128,2 @@ this.manager.consolidate(consolidate); | ||
* Connect a client to the coordinator. | ||
* | ||
* @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect | ||
@@ -124,0 +130,0 @@ */ |
@@ -57,3 +57,4 @@ import { Query, and, create, isBetween, scaleTransform, sql } from '@uwdata/mosaic-sql'; | ||
const { source } = active; | ||
if (source && source === this.activeView?.source) return true; // we're good! | ||
// exit early if indexes already set up for active view | ||
if (source && source === this.activeView?.source) return true; | ||
@@ -79,3 +80,3 @@ this.clear(); | ||
const query = client.query(sel.predicate(client)) | ||
.select({ ...activeView.columns, ...index.count }) | ||
.select({ ...activeView.columns, ...index.aux }) | ||
.groupby(Object.keys(activeView.columns)); | ||
@@ -100,2 +101,5 @@ | ||
} | ||
// index creation successful | ||
return true; | ||
} | ||
@@ -185,6 +189,9 @@ | ||
const dims = []; | ||
let count; | ||
const aux = {}; // auxiliary columns needed by aggregates | ||
let auxAs; | ||
for (const { as, expr: { aggregate } } of q.select()) { | ||
switch (aggregate?.toUpperCase?.()) { | ||
for (const entry of q.select()) { | ||
const { as, expr: { aggregate, args } } = entry; | ||
const op = aggregate?.toUpperCase?.(); | ||
switch (op) { | ||
case 'COUNT': | ||
@@ -195,10 +202,24 @@ case 'SUM': | ||
case 'AVG': | ||
count = '_count_'; | ||
aggr.push({ [as]: sql`(SUM("${as}" * ${count}) / SUM(${count}))::DOUBLE` }); | ||
aux[auxAs = '__count__'] = sql`COUNT(*)`; | ||
aggr.push({ [as]: sql`(SUM("${as}" * ${auxAs}) / SUM(${auxAs}))::DOUBLE` }); | ||
break; | ||
case 'ARG_MAX': | ||
aux[auxAs = `__max_${as}__`] = sql`MAX(${args[1]})`; | ||
aggr.push({ [as]: sql`ARG_MAX("${as}", ${auxAs})` }); | ||
break; | ||
case 'ARG_MIN': | ||
aux[auxAs = `__min_${as}__`] = sql`MIN(${args[1]})`; | ||
aggr.push({ [as]: sql`ARG_MIN("${as}", ${auxAs})` }); | ||
break; | ||
// aggregates that commute directly | ||
case 'MAX': | ||
aggr.push({ [as]: sql`MAX("${as}")` }); | ||
break; | ||
case 'MIN': | ||
aggr.push({ [as]: sql`MIN("${as}")` }); | ||
case 'BIT_AND': | ||
case 'BIT_OR': | ||
case 'BIT_XOR': | ||
case 'BOOL_AND': | ||
case 'BOOL_OR': | ||
case 'PRODUCT': | ||
aggr.push({ [as]: sql`${op}("${as}")` }); | ||
break; | ||
@@ -211,8 +232,3 @@ default: | ||
return { | ||
aggr, | ||
dims, | ||
count: count ? { [count]: sql`COUNT(*)` } : {}, | ||
from | ||
}; | ||
return { aggr, dims, aux, from }; | ||
} | ||
@@ -219,0 +235,0 @@ |
@@ -49,5 +49,11 @@ import { DataCubeIndexer } from './DataCubeIndexer.js'; | ||
/** | ||
* Internal method to process a selection update. | ||
* The return value is passed as a selection callback value. | ||
* @returns {Promise} A Promise that resolves when the update completes. | ||
*/ | ||
update() { | ||
const { mc, indexer, clients, selection } = this; | ||
return indexer?.index(clients) | ||
const hasIndex = indexer?.index(clients); | ||
return hasIndex | ||
? indexer.update() | ||
@@ -54,0 +60,0 @@ : defaultUpdate(mc, clients, selection); |
@@ -51,2 +51,3 @@ import { throttle } from './util/throttle.js'; | ||
* Return an array of fields queried by this client. | ||
* @returns {object[]|null} The fields to retrieve info for. | ||
*/ | ||
@@ -59,5 +60,6 @@ fields() { | ||
* Called by the coordinator to set the field info for this client. | ||
* @param {*} info The field info result. | ||
* @returns {this} | ||
*/ | ||
fieldInfo() { | ||
fieldInfo(info) { // eslint-disable-line no-unused-vars | ||
return this; | ||
@@ -68,4 +70,6 @@ } | ||
* Return a query specifying the data needed by this client. | ||
* @param {*} [filter] The filtering criteria to apply in the query. | ||
* @returns {*} The client query | ||
*/ | ||
query() { | ||
query(filter) { // eslint-disable-line no-unused-vars | ||
return null; | ||
@@ -76,2 +80,3 @@ } | ||
* Called by the coordinator to inform the client that a query is pending. | ||
* @returns {this} | ||
*/ | ||
@@ -84,7 +89,6 @@ queryPending() { | ||
* Called by the coordinator to return a query result. | ||
* | ||
* @param {*} data the query result | ||
* @param {*} data The query result. | ||
* @returns {this} | ||
*/ | ||
queryResult() { | ||
queryResult(data) { // eslint-disable-line no-unused-vars | ||
return this; | ||
@@ -95,2 +99,4 @@ } | ||
* Called by the coordinator to report a query execution error. | ||
* @param {*} error | ||
* @returns {this} | ||
*/ | ||
@@ -125,2 +131,4 @@ queryError(error) { | ||
* For example to (re-)render an interface component. | ||
* | ||
* @returns {this | Promise<any>} | ||
*/ | ||
@@ -127,0 +135,0 @@ update() { |
@@ -7,3 +7,3 @@ import { AsyncDispatch } from './util/AsyncDispatch.js'; | ||
* @param {*} x The value to test. | ||
* @returns {boolean} True if the input is a Param, false otherwise. | ||
* @returns {x is Param} True if the input is a Param, false otherwise. | ||
*/ | ||
@@ -47,3 +47,5 @@ export function isParam(x) { | ||
const p = new Param(); | ||
const update = () => p.update(values.map(v => isParam(v) ? v.value : v)); | ||
const update = () => { | ||
p.update(values.map(v => isParam(v) ? v.value : v)); | ||
}; | ||
update(); | ||
@@ -50,0 +52,0 @@ values.forEach(v => isParam(v) ? v.addEventListener('value', update) : 0); |
@@ -8,2 +8,3 @@ import { Query, Ref, isDescribeQuery } from '@uwdata/mosaic-sql'; | ||
: typeof setImmediate !== 'undefined' ? setImmediate : setTimeout; | ||
// @ts-ignore | ||
return method(callback); | ||
@@ -86,3 +87,5 @@ } | ||
if ( | ||
// @ts-ignore | ||
query.orderby().length || query.where().length || | ||
// @ts-ignore | ||
query.qualify().length || query.having().length | ||
@@ -102,5 +105,8 @@ ) { | ||
const groupby = query.groupby(); | ||
// @ts-ignore | ||
if (groupby.length) { | ||
const map = {}; // expression map (as -> expr) | ||
// @ts-ignore | ||
query.select().forEach(({ as, expr }) => map[as] = expr); | ||
// @ts-ignore | ||
q.$groupby(groupby.map(e => (e instanceof Ref && map[e.column]) || e)); | ||
@@ -107,0 +113,0 @@ } |
@@ -7,3 +7,3 @@ import { or } from '@uwdata/mosaic-sql'; | ||
* @param {*} x The value to test. | ||
* @returns {boolean} True if the input is a Selection, false otherwise. | ||
* @returns {x is Selection} True if the input is a Selection, false otherwise. | ||
*/ | ||
@@ -80,3 +80,3 @@ export function isSelection(x) { | ||
* Create a cloned copy of this Selection instance. | ||
* @returns {this} A clone of this selection. | ||
* @returns {Selection} A clone of this selection. | ||
*/ | ||
@@ -93,3 +93,3 @@ clone() { | ||
* @param {*} source The clause source to remove. | ||
* @returns {this} A cloned and updated Selection. | ||
* @returns {Selection} A cloned and updated Selection. | ||
*/ | ||
@@ -174,5 +174,5 @@ remove(source) { | ||
* @param {string} type The event type. | ||
* @param {*} value The input event value. | ||
* @returns {*} For value-typed events, returns a dispatch queue filter | ||
* function. Otherwise returns null. | ||
* @param {*} value The new event value that will be enqueued. | ||
* @returns {(value: *) => boolean|null} For value-typed events, | ||
* returns a dispatch queue filter function. Otherwise returns null. | ||
*/ | ||
@@ -292,3 +292,4 @@ emitQueueFilter(type, value) { | ||
} | ||
return null; | ||
} | ||
} |
@@ -19,3 +19,3 @@ /** | ||
* @param {string} type The event type. | ||
* @param {(value: *) => Promise?} callback The event handler | ||
* @param {(value: *) => void | Promise} callback The event handler | ||
* callback function to add. If the callback has already been | ||
@@ -39,3 +39,3 @@ * added for the event type, this method has no effect. | ||
* @param {string} type The event type. | ||
* @param {(value: *) => Promise?} callback The event handler | ||
* @param {(value: *) => void | Promise} callback The event handler | ||
* callback function to remove. | ||
@@ -69,2 +69,3 @@ */ | ||
* queued events are filtered) | ||
* @param {string} type The event type. | ||
* @param {*} value The new event value that will be enqueued. | ||
@@ -74,3 +75,3 @@ * @returns {(value: *) => boolean|null} A dispatch queue filter | ||
*/ | ||
emitQueueFilter() { | ||
emitQueueFilter(type, value) { // eslint-disable-line no-unused-vars | ||
// removes all pending items | ||
@@ -101,2 +102,4 @@ return null; | ||
if (entry.pending) { | ||
// an earlier emit is still processing | ||
// enqueue the current update, possibly filtering other pending updates | ||
entry.queue.enqueue(value, this.emitQueueFilter(type, value)); | ||
@@ -107,11 +110,11 @@ } else { | ||
if (callbacks?.size) { | ||
const promise = Promise | ||
.allSettled(Array.from(callbacks, callback => callback(event))) | ||
.then(() => { | ||
entry.pending = null; | ||
if (!queue.isEmpty()) { | ||
this.emit(type, queue.dequeue()); | ||
} | ||
}); | ||
entry.pending = promise; | ||
// broadcast update to callbacks, which may return promises | ||
// wait until promises resolve, then process pending updates | ||
const callbackValues = Array.from(callbacks, cb => cb(event)); | ||
entry.pending = Promise.allSettled(callbackValues).then(() => { | ||
entry.pending = null; | ||
if (!queue.isEmpty()) { | ||
this.emit(type, queue.dequeue()); | ||
} | ||
}); | ||
} | ||
@@ -118,0 +121,0 @@ } |
@@ -1,6 +0,2 @@ | ||
// arrow type ids | ||
const INTEGER = 2; | ||
const FLOAT = 3; | ||
const DECIMAL = 7; | ||
const TIMESTAMP = 10; | ||
import { DataType } from 'apache-arrow'; | ||
@@ -12,3 +8,3 @@ /** | ||
* @param {*} values The value to test | ||
* @returns true if the value duck types as Apache Arrow data | ||
* @returns {values is import('apache-arrow').Table} true if the value duck types as Apache Arrow data | ||
*/ | ||
@@ -20,15 +16,10 @@ export function isArrowTable(values) { | ||
/** | ||
* Return a JavaScript array type for an Apache Arrow column type. | ||
* @param {*} type an Apache Arrow column type | ||
* @returns a JavaScript array constructor | ||
*/ | ||
* Return a JavaScript array type for an Apache Arrow column type. | ||
* @param {DataType} type an Apache Arrow column type | ||
* @returns a JavaScript array constructor | ||
*/ | ||
export function convertArrowArrayType(type) { | ||
switch (type.typeId) { | ||
case INTEGER: | ||
case FLOAT: | ||
case DECIMAL: | ||
return Float64Array; | ||
default: | ||
return Array; | ||
} | ||
return DataType.isInt(type) || DataType.isFloat(type) || DataType.isDecimal(type) | ||
? Float64Array | ||
: Array; | ||
} | ||
@@ -42,10 +33,8 @@ | ||
* Otherwise, the default Arrow values are used. | ||
* @param {*} type an Apache Arrow column type | ||
* @param {DataType} type an Apache Arrow column type | ||
* @returns a value conversion function | ||
*/ | ||
export function convertArrowValue(type) { | ||
const { typeId } = type; | ||
// map timestamp numbers to date objects | ||
if (typeId === TIMESTAMP) { | ||
if (DataType.isTimestamp(type)) { | ||
return v => v == null ? v : new Date(v); | ||
@@ -55,3 +44,3 @@ } | ||
// map bigint to number | ||
if (typeId === INTEGER && type.bitWidth >= 64) { | ||
if (DataType.isInt(type) && type.bitWidth >= 64) { | ||
return v => v == null ? v : Number(v); | ||
@@ -61,3 +50,3 @@ } | ||
// map decimal to number | ||
if (typeId === DECIMAL) { | ||
if (DataType.isDecimal(type)) { | ||
const scale = 1 / Math.pow(10, type.scale); | ||
@@ -82,6 +71,5 @@ return v => v == null ? v : decimalToNumber(v, scale); | ||
const { type } = column; | ||
const { typeId } = type; | ||
// map timestamp numbers to date objects | ||
if (typeId === TIMESTAMP) { | ||
if (DataType.isTimestamp(type)) { | ||
const size = column.length; | ||
@@ -97,3 +85,3 @@ const array = new Array(size); | ||
// map bigint to number | ||
if (typeId === INTEGER && type.bitWidth >= 64) { | ||
if (DataType.isInt(type) && type.bitWidth >= 64) { | ||
const size = column.length; | ||
@@ -109,3 +97,3 @@ const array = new Float64Array(size); | ||
// map decimal to number | ||
if (typeId === DECIMAL) { | ||
if (DataType.isDecimal(type)) { | ||
const scale = 1 / Math.pow(10, type.scale); | ||
@@ -135,6 +123,6 @@ const size = column.length; | ||
* Note: if the value is sufficiently large the conversion may be lossy! | ||
* @param {Uint32Array} v a fixed decimal value | ||
* @param {Uint32Array & { signed: boolean }} v a fixed decimal value | ||
* @param {number} scale a scale factor, corresponding to the | ||
* number of fractional decimal digits in the fixed point value | ||
* @returns the resulting number | ||
* @returns {number} the resulting number | ||
*/ | ||
@@ -141,0 +129,0 @@ function decimalToNumber(v, scale) { |
@@ -27,2 +27,3 @@ export function jsType(type) { | ||
case 'UUID': | ||
case 'JSON': | ||
return 'string'; | ||
@@ -29,0 +30,0 @@ case 'ARRAY': |
@@ -5,5 +5,6 @@ export function queryResult() { | ||
const p = new Promise((r, e) => { resolve = r; reject = e; }); | ||
p.fulfill = value => (resolve(value), p); | ||
p.reject = err => (reject(err), p); | ||
return p; | ||
return Object.assign(p, { | ||
fulfill: value => (resolve(value), p), | ||
reject: err => (reject(err), p) | ||
}); | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1392912
29552
1
+ Added@uwdata/mosaic-sql@0.8.0(transitive)
- Removed@uwdata/mosaic-sql@0.7.0(transitive)
Updated@uwdata/mosaic-sql@^0.8.0
Updatedapache-arrow@^15.0.2