@uwdata/mosaic-core 0.0.1 → 0.1.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 +28 -0
- package/README.md +2 -2
- package/dist/mosaic-core.js +337 -226
- package/dist/mosaic-core.min.js +7 -7
- package/package.json +4 -3
- package/src/Catalog.js +24 -38
- package/src/Coordinator.js +32 -32
- package/src/DataTileIndexer.js +9 -7
- package/src/FilterGroup.js +3 -3
- package/src/MosaicClient.js +64 -0
- package/src/{Signal.js → Param.js} +11 -5
- package/src/QueryCache.js +13 -13
- package/src/Selection.js +2 -2
- package/src/index.js +2 -1
- package/src/util/distinct.js +14 -0
- package/src/util/summarize.js +23 -0
- package/src/util/throttle.js +22 -4
- package/src/util/void-logger.js +9 -0
package/dist/mosaic-core.js
CHANGED
|
@@ -4,41 +4,6 @@ var __export = (target, all2) => {
|
|
|
4
4
|
__defProp(target, name, { get: all2[name], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
// src/MosaicClient.js
|
|
8
|
-
var MosaicClient = class {
|
|
9
|
-
constructor(filterSelection) {
|
|
10
|
-
this._filterBy = filterSelection;
|
|
11
|
-
}
|
|
12
|
-
get filterBy() {
|
|
13
|
-
return this._filterBy;
|
|
14
|
-
}
|
|
15
|
-
get filterIndexable() {
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
fields() {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
fieldStats() {
|
|
22
|
-
return this;
|
|
23
|
-
}
|
|
24
|
-
query() {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
queryPending() {
|
|
28
|
-
return this;
|
|
29
|
-
}
|
|
30
|
-
queryResult() {
|
|
31
|
-
return this;
|
|
32
|
-
}
|
|
33
|
-
queryError(error) {
|
|
34
|
-
console.error(error);
|
|
35
|
-
return this;
|
|
36
|
-
}
|
|
37
|
-
update() {
|
|
38
|
-
return this;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
7
|
// ../../node_modules/tslib/tslib.es6.js
|
|
43
8
|
function __rest(s, e) {
|
|
44
9
|
var t = {};
|
|
@@ -11532,6 +11497,44 @@ function socketClient(uri = "ws://localhost:3000/") {
|
|
|
11532
11497
|
};
|
|
11533
11498
|
}
|
|
11534
11499
|
|
|
11500
|
+
// src/util/js-type.js
|
|
11501
|
+
function jsType(type) {
|
|
11502
|
+
switch (type) {
|
|
11503
|
+
case "BIGINT":
|
|
11504
|
+
case "HUGEINT":
|
|
11505
|
+
case "INTEGER":
|
|
11506
|
+
case "SMALLINT":
|
|
11507
|
+
case "TINYINT":
|
|
11508
|
+
case "UBIGINT":
|
|
11509
|
+
case "UINTEGER":
|
|
11510
|
+
case "USMALLINT":
|
|
11511
|
+
case "UTINYINT":
|
|
11512
|
+
case "DOUBLE":
|
|
11513
|
+
case "FLOAT":
|
|
11514
|
+
case "REAL":
|
|
11515
|
+
case "DECIMAL":
|
|
11516
|
+
return "number";
|
|
11517
|
+
case "DATE":
|
|
11518
|
+
case "TIMESTAMP":
|
|
11519
|
+
case "TIMESTAMPTZ":
|
|
11520
|
+
case "TIME":
|
|
11521
|
+
return "date";
|
|
11522
|
+
case "BOOLEAN":
|
|
11523
|
+
return "boolean";
|
|
11524
|
+
case "VARCHAR":
|
|
11525
|
+
case "UUID":
|
|
11526
|
+
return "string";
|
|
11527
|
+
case "LIST":
|
|
11528
|
+
return "array";
|
|
11529
|
+
case "BLOB":
|
|
11530
|
+
case "STRUCT":
|
|
11531
|
+
case "MAP":
|
|
11532
|
+
return "object";
|
|
11533
|
+
default:
|
|
11534
|
+
throw new Error(`Unsupported type: ${type}`);
|
|
11535
|
+
}
|
|
11536
|
+
}
|
|
11537
|
+
|
|
11535
11538
|
// ../sql/src/ref.js
|
|
11536
11539
|
var Ref = class {
|
|
11537
11540
|
constructor(table, column2) {
|
|
@@ -11568,23 +11571,6 @@ function relation(name) {
|
|
|
11568
11571
|
function column(table, column2) {
|
|
11569
11572
|
return arguments.length === 1 ? new Ref(null, table) : new Ref(table, column2);
|
|
11570
11573
|
}
|
|
11571
|
-
function expr(sql, columns, label) {
|
|
11572
|
-
return {
|
|
11573
|
-
expr: sql,
|
|
11574
|
-
label,
|
|
11575
|
-
toString: () => `${sql}`,
|
|
11576
|
-
get columns() {
|
|
11577
|
-
return columns || [];
|
|
11578
|
-
}
|
|
11579
|
-
};
|
|
11580
|
-
}
|
|
11581
|
-
function transform(func2, label) {
|
|
11582
|
-
return (value) => expr(
|
|
11583
|
-
func2(value),
|
|
11584
|
-
asColumn(value).columns,
|
|
11585
|
-
label
|
|
11586
|
-
);
|
|
11587
|
-
}
|
|
11588
11574
|
|
|
11589
11575
|
// ../sql/src/to-sql.js
|
|
11590
11576
|
function toSQL(value) {
|
|
@@ -11609,6 +11595,27 @@ function literalToSQL(value) {
|
|
|
11609
11595
|
}
|
|
11610
11596
|
}
|
|
11611
11597
|
|
|
11598
|
+
// ../sql/src/expression.js
|
|
11599
|
+
function isExpression(e) {
|
|
11600
|
+
return e instanceof SQLExpression;
|
|
11601
|
+
}
|
|
11602
|
+
var SQLExpression = class {
|
|
11603
|
+
constructor(sql2, columns, label) {
|
|
11604
|
+
this.expr = sql2;
|
|
11605
|
+
this.label = label;
|
|
11606
|
+
this.columns = columns || [];
|
|
11607
|
+
}
|
|
11608
|
+
toString() {
|
|
11609
|
+
return `${this.expr}`;
|
|
11610
|
+
}
|
|
11611
|
+
};
|
|
11612
|
+
function expr(sql2, columns, label) {
|
|
11613
|
+
return new SQLExpression(sql2, columns, label);
|
|
11614
|
+
}
|
|
11615
|
+
function transform(func2, label) {
|
|
11616
|
+
return (value) => expr(func2(value), asColumn(value).columns, label);
|
|
11617
|
+
}
|
|
11618
|
+
|
|
11612
11619
|
// ../sql/src/compare.js
|
|
11613
11620
|
function extractColumns(...args) {
|
|
11614
11621
|
return args.flat().flatMap((arg) => arg?.columns || []);
|
|
@@ -11769,10 +11776,10 @@ var Aggregate = class {
|
|
|
11769
11776
|
toString() {
|
|
11770
11777
|
const { aggregate, args, isDistinct: isDistinct2, filter } = this;
|
|
11771
11778
|
const arg = args.length === 0 ? "*" : args.map(toSQL).join(", ");
|
|
11772
|
-
const
|
|
11779
|
+
const distinct2 = isDistinct2 ? "DISTINCT " : "";
|
|
11773
11780
|
const where = filter ? ` FILTER (WHERE ${toSQL(filter)})` : "";
|
|
11774
11781
|
const cast = aggregate === "COUNT" ? "::INTEGER" : "";
|
|
11775
|
-
return where && cast ? `(${aggregate}(${
|
|
11782
|
+
return where && cast ? `(${aggregate}(${distinct2}${arg})${where})${cast}` : `${aggregate}(${distinct2}${arg})${where}${cast}`;
|
|
11776
11783
|
}
|
|
11777
11784
|
};
|
|
11778
11785
|
function agg(op) {
|
|
@@ -11929,7 +11936,7 @@ var Query = class {
|
|
|
11929
11936
|
list.push({ as: e, from: asRelation(e) });
|
|
11930
11937
|
} else if (e instanceof Ref) {
|
|
11931
11938
|
list.push({ as: e.table, from: e });
|
|
11932
|
-
} else if (isQuery(e) ||
|
|
11939
|
+
} else if (isQuery(e) || isExpression(e)) {
|
|
11933
11940
|
list.push({ from: e });
|
|
11934
11941
|
} else if (Array.isArray(e)) {
|
|
11935
11942
|
list.push({ as: unquote(e[0]), from: asRelation(e[1]) });
|
|
@@ -12073,7 +12080,7 @@ var Query = class {
|
|
|
12073
12080
|
toString() {
|
|
12074
12081
|
const {
|
|
12075
12082
|
select,
|
|
12076
|
-
distinct,
|
|
12083
|
+
distinct: distinct2,
|
|
12077
12084
|
from,
|
|
12078
12085
|
sample,
|
|
12079
12086
|
where,
|
|
@@ -12086,60 +12093,60 @@ var Query = class {
|
|
|
12086
12093
|
offset,
|
|
12087
12094
|
with: cte
|
|
12088
12095
|
} = this.query;
|
|
12089
|
-
const
|
|
12096
|
+
const sql2 = [];
|
|
12090
12097
|
if (cte.length) {
|
|
12091
12098
|
const list = cte.map(({ as, query }) => `"${as}" AS (${query})`);
|
|
12092
|
-
|
|
12099
|
+
sql2.push(`WITH ${list.join(", ")}`);
|
|
12093
12100
|
}
|
|
12094
12101
|
const sels = select.map(
|
|
12095
12102
|
({ as, expr: expr2 }) => isColumnRefFor(expr2, as) && !expr2.table ? `${expr2}` : `${expr2} AS "${as}"`
|
|
12096
12103
|
);
|
|
12097
|
-
|
|
12104
|
+
sql2.push(`SELECT${distinct2 ? " DISTINCT" : ""} ${sels.join(", ")}`);
|
|
12098
12105
|
if (from.length) {
|
|
12099
12106
|
const rels = from.map(({ as, from: from2 }) => {
|
|
12100
12107
|
const rel = isQuery(from2) ? `(${from2})` : `${from2}`;
|
|
12101
12108
|
return !as || as === from2.table ? rel : `${rel} AS "${as}"`;
|
|
12102
12109
|
});
|
|
12103
|
-
|
|
12110
|
+
sql2.push(`FROM ${rels.join(", ")}`);
|
|
12104
12111
|
}
|
|
12105
12112
|
if (sample) {
|
|
12106
12113
|
const { rows, perc, method, seed } = sample;
|
|
12107
12114
|
const size = rows ? `${rows} ROWS` : `${perc} PERCENT`;
|
|
12108
12115
|
const how = method ? ` (${method}${seed != null ? `, ${seed}` : ""})` : "";
|
|
12109
|
-
|
|
12116
|
+
sql2.push(`USING SAMPLE ${size}${how}`);
|
|
12110
12117
|
}
|
|
12111
12118
|
if (where.length) {
|
|
12112
12119
|
const clauses = where.map(String).filter((x2) => x2).join(" AND ");
|
|
12113
12120
|
if (clauses)
|
|
12114
|
-
|
|
12121
|
+
sql2.push(`WHERE ${clauses}`);
|
|
12115
12122
|
}
|
|
12116
12123
|
if (groupby.length) {
|
|
12117
|
-
|
|
12124
|
+
sql2.push(`GROUP BY ${groupby.join(", ")}`);
|
|
12118
12125
|
}
|
|
12119
12126
|
if (having.length) {
|
|
12120
12127
|
const clauses = having.map(String).filter((x2) => x2).join(" AND ");
|
|
12121
12128
|
if (clauses)
|
|
12122
|
-
|
|
12129
|
+
sql2.push(`HAVING ${clauses}`);
|
|
12123
12130
|
}
|
|
12124
12131
|
if (window.length) {
|
|
12125
12132
|
const windows = window.map(({ as, expr: expr2 }) => `"${as}" AS (${expr2})`);
|
|
12126
|
-
|
|
12133
|
+
sql2.push(`WINDOW ${windows.join(", ")}`);
|
|
12127
12134
|
}
|
|
12128
12135
|
if (qualify.length) {
|
|
12129
12136
|
const clauses = qualify.map(String).filter((x2) => x2).join(" AND ");
|
|
12130
12137
|
if (clauses)
|
|
12131
|
-
|
|
12138
|
+
sql2.push(`QUALIFY ${clauses}`);
|
|
12132
12139
|
}
|
|
12133
12140
|
if (orderby.length) {
|
|
12134
|
-
|
|
12141
|
+
sql2.push(`ORDER BY ${orderby.join(", ")}`);
|
|
12135
12142
|
}
|
|
12136
12143
|
if (Number.isFinite(limit)) {
|
|
12137
|
-
|
|
12144
|
+
sql2.push(`LIMIT ${limit}`);
|
|
12138
12145
|
}
|
|
12139
12146
|
if (Number.isFinite(offset)) {
|
|
12140
|
-
|
|
12147
|
+
sql2.push(`OFFSET ${offset}`);
|
|
12141
12148
|
}
|
|
12142
|
-
return
|
|
12149
|
+
return sql2.join(" ");
|
|
12143
12150
|
}
|
|
12144
12151
|
};
|
|
12145
12152
|
var SetOperation = class {
|
|
@@ -12190,17 +12197,17 @@ var SetOperation = class {
|
|
|
12190
12197
|
}
|
|
12191
12198
|
toString() {
|
|
12192
12199
|
const { op, queries, query: { orderby, limit, offset } } = this;
|
|
12193
|
-
const
|
|
12200
|
+
const sql2 = [queries.join(` ${op} `)];
|
|
12194
12201
|
if (orderby.length) {
|
|
12195
|
-
|
|
12202
|
+
sql2.push(`ORDER BY ${orderby.join(", ")}`);
|
|
12196
12203
|
}
|
|
12197
12204
|
if (Number.isFinite(limit)) {
|
|
12198
|
-
|
|
12205
|
+
sql2.push(`LIMIT ${limit}`);
|
|
12199
12206
|
}
|
|
12200
12207
|
if (Number.isFinite(offset)) {
|
|
12201
|
-
|
|
12208
|
+
sql2.push(`OFFSET ${offset}`);
|
|
12202
12209
|
}
|
|
12203
|
-
return
|
|
12210
|
+
return sql2.join(" ");
|
|
12204
12211
|
}
|
|
12205
12212
|
};
|
|
12206
12213
|
function isQuery(value) {
|
|
@@ -12213,54 +12220,32 @@ function isDoubleQuoted(s) {
|
|
|
12213
12220
|
return s[0] === '"' && s[s.length - 1] === '"';
|
|
12214
12221
|
}
|
|
12215
12222
|
|
|
12216
|
-
// src/util/
|
|
12217
|
-
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12228
|
-
|
|
12229
|
-
|
|
12230
|
-
|
|
12231
|
-
case "DECIMAL":
|
|
12232
|
-
return "number";
|
|
12233
|
-
case "DATE":
|
|
12234
|
-
case "TIMESTAMP":
|
|
12235
|
-
case "TIMESTAMPTZ":
|
|
12236
|
-
case "TIME":
|
|
12237
|
-
return "date";
|
|
12238
|
-
case "BOOLEAN":
|
|
12239
|
-
return "boolean";
|
|
12240
|
-
case "VARCHAR":
|
|
12241
|
-
case "UUID":
|
|
12242
|
-
return "string";
|
|
12243
|
-
case "LIST":
|
|
12244
|
-
return "array";
|
|
12245
|
-
case "BLOB":
|
|
12246
|
-
case "STRUCT":
|
|
12247
|
-
case "MAP":
|
|
12248
|
-
return "object";
|
|
12249
|
-
default:
|
|
12250
|
-
throw new Error(`Unsupported type: ${type}`);
|
|
12251
|
-
}
|
|
12223
|
+
// src/util/summarize.js
|
|
12224
|
+
var Count = "count";
|
|
12225
|
+
var Nulls = "nulls";
|
|
12226
|
+
var Max = "max";
|
|
12227
|
+
var Min = "min";
|
|
12228
|
+
var Distinct = "distinct";
|
|
12229
|
+
var statMap = {
|
|
12230
|
+
[Count]: count,
|
|
12231
|
+
[Distinct]: (column2) => count(column2).distinct(),
|
|
12232
|
+
[Max]: max,
|
|
12233
|
+
[Min]: min,
|
|
12234
|
+
[Nulls]: (column2) => count().where(isNull(column2))
|
|
12235
|
+
};
|
|
12236
|
+
function summarize({ table, column: column2 }, stats) {
|
|
12237
|
+
return Query.from(table).select(stats.map((s) => [s, statMap[s](column2)]));
|
|
12252
12238
|
}
|
|
12253
12239
|
|
|
12254
12240
|
// src/Catalog.js
|
|
12255
12241
|
var object = () => /* @__PURE__ */ Object.create(null);
|
|
12256
12242
|
var Catalog = class {
|
|
12257
|
-
constructor(
|
|
12258
|
-
this.mc =
|
|
12243
|
+
constructor(coordinator2) {
|
|
12244
|
+
this.mc = coordinator2;
|
|
12259
12245
|
this.clear();
|
|
12260
12246
|
}
|
|
12261
12247
|
clear() {
|
|
12262
12248
|
this.tables = object();
|
|
12263
|
-
this.fields = object();
|
|
12264
12249
|
}
|
|
12265
12250
|
async tableInfo(table) {
|
|
12266
12251
|
const cache = this.tables;
|
|
@@ -12268,56 +12253,42 @@ var Catalog = class {
|
|
|
12268
12253
|
return cache[table];
|
|
12269
12254
|
}
|
|
12270
12255
|
const q2 = this.mc.query(
|
|
12271
|
-
`
|
|
12272
|
-
{ type: "json" }
|
|
12256
|
+
`DESCRIBE "${table}"`,
|
|
12257
|
+
{ type: "json", cache: false }
|
|
12273
12258
|
);
|
|
12274
12259
|
return cache[table] = q2.then((result) => {
|
|
12275
12260
|
const columns = object();
|
|
12276
12261
|
for (const entry of result) {
|
|
12277
|
-
columns[entry.
|
|
12262
|
+
columns[entry.column_name] = {
|
|
12263
|
+
table,
|
|
12264
|
+
column: entry.column_name,
|
|
12265
|
+
sqlType: entry.column_type,
|
|
12266
|
+
type: jsType(entry.column_type),
|
|
12267
|
+
nullable: entry.null === "YES"
|
|
12268
|
+
};
|
|
12278
12269
|
}
|
|
12279
12270
|
return columns;
|
|
12280
12271
|
});
|
|
12281
12272
|
}
|
|
12282
|
-
async fieldInfo(table, column2) {
|
|
12283
|
-
const
|
|
12284
|
-
const colInfo =
|
|
12273
|
+
async fieldInfo({ table, column: column2, stats }) {
|
|
12274
|
+
const tableInfo = await this.tableInfo(table);
|
|
12275
|
+
const colInfo = tableInfo[column2];
|
|
12285
12276
|
if (colInfo == null)
|
|
12286
12277
|
return;
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
const promise = this.mc.query(
|
|
12293
|
-
Query.from(table).select({
|
|
12294
|
-
rows: count(),
|
|
12295
|
-
nulls: count().where(isNull(column2)),
|
|
12296
|
-
min: min(column2),
|
|
12297
|
-
max: max(column2)
|
|
12298
|
-
}, { cache: false })
|
|
12299
|
-
).then((result) => {
|
|
12300
|
-
const [stats] = Array.from(result);
|
|
12301
|
-
return { table, column: column2, type: colInfo.jstype, ...stats };
|
|
12302
|
-
});
|
|
12303
|
-
return cache[key] = promise;
|
|
12278
|
+
if (!stats?.length)
|
|
12279
|
+
return colInfo;
|
|
12280
|
+
const result = await this.mc.query(summarize(colInfo, stats));
|
|
12281
|
+
const info = { ...colInfo, ...Array.from(result)[0] };
|
|
12282
|
+
return info;
|
|
12304
12283
|
}
|
|
12305
12284
|
async queryFields(fields) {
|
|
12306
12285
|
const list = await resolveFields(this, fields);
|
|
12307
|
-
const data = await Promise.all(
|
|
12308
|
-
list.map((f2) => this.fieldInfo(f2.table, f2.column))
|
|
12309
|
-
);
|
|
12286
|
+
const data = await Promise.all(list.map((f2) => this.fieldInfo(f2)));
|
|
12310
12287
|
return data.filter((x2) => x2);
|
|
12311
12288
|
}
|
|
12312
12289
|
};
|
|
12313
12290
|
async function resolveFields(catalog, list) {
|
|
12314
|
-
|
|
12315
|
-
const table = list[0].table;
|
|
12316
|
-
const info = await catalog.tableInfo(table);
|
|
12317
|
-
return Object.keys(info).map((column2) => ({ table, column: column2 }));
|
|
12318
|
-
} else {
|
|
12319
|
-
return list;
|
|
12320
|
-
}
|
|
12291
|
+
return list.length === 1 && list[0].column === "*" ? Object.values(await catalog.tableInfo(list[0].table)) : list;
|
|
12321
12292
|
}
|
|
12322
12293
|
|
|
12323
12294
|
// src/util/hash.js
|
|
@@ -12383,9 +12354,10 @@ var DataTileIndexer = class {
|
|
|
12383
12354
|
const activeView = this.activeView = getActiveView(active);
|
|
12384
12355
|
if (!activeView)
|
|
12385
12356
|
return false;
|
|
12386
|
-
|
|
12357
|
+
this.mc.logger().warn("DATA TILE INDEX CONSTRUCTION");
|
|
12387
12358
|
const sel = this.selection.clone().update({ source });
|
|
12388
12359
|
const indices = this.indices = /* @__PURE__ */ new Map();
|
|
12360
|
+
const promises = [];
|
|
12389
12361
|
for (const client of clients) {
|
|
12390
12362
|
if (sel.cross && skipClient(client, active))
|
|
12391
12363
|
continue;
|
|
@@ -12396,13 +12368,13 @@ var DataTileIndexer = class {
|
|
|
12396
12368
|
const cols = Object.values(activeView.columns).map((c) => c.columns[0]);
|
|
12397
12369
|
subqueryPushdown(subq, cols);
|
|
12398
12370
|
}
|
|
12399
|
-
const
|
|
12400
|
-
const id = (fnv_hash(
|
|
12371
|
+
const sql2 = query.toString();
|
|
12372
|
+
const id = (fnv_hash(sql2) >>> 0).toString(16);
|
|
12401
12373
|
const table = `tile_index_${id}`;
|
|
12402
12374
|
indices.set(client, { table, ...index });
|
|
12403
|
-
createIndex(this.mc, table,
|
|
12375
|
+
promises.push(createIndex(this.mc, table, sql2));
|
|
12404
12376
|
}
|
|
12405
|
-
return
|
|
12377
|
+
return promises;
|
|
12406
12378
|
}
|
|
12407
12379
|
async update() {
|
|
12408
12380
|
const { clients, selection, activeView } = this;
|
|
@@ -12455,38 +12427,38 @@ function getActiveView(clause) {
|
|
|
12455
12427
|
}
|
|
12456
12428
|
function binInterval(scale) {
|
|
12457
12429
|
const { type, domain, range: range2 } = scale;
|
|
12458
|
-
let lift,
|
|
12430
|
+
let lift, sql2;
|
|
12459
12431
|
switch (type) {
|
|
12460
12432
|
case "linear":
|
|
12461
12433
|
lift = identity;
|
|
12462
|
-
|
|
12434
|
+
sql2 = asColumn;
|
|
12463
12435
|
break;
|
|
12464
12436
|
case "log":
|
|
12465
12437
|
lift = Math.log;
|
|
12466
|
-
|
|
12438
|
+
sql2 = (c) => `LN(${asColumn(c)})`;
|
|
12467
12439
|
break;
|
|
12468
12440
|
case "symlog":
|
|
12469
12441
|
lift = (x2) => Math.sign(x2) * Math.log1p(Math.abs(x2));
|
|
12470
|
-
|
|
12442
|
+
sql2 = (c) => (c = asColumn(c), `SIGN(${c}) * LN(1 + ABS(${c}))`);
|
|
12471
12443
|
break;
|
|
12472
12444
|
case "sqrt":
|
|
12473
12445
|
lift = Math.sqrt;
|
|
12474
|
-
|
|
12446
|
+
sql2 = (c) => `SQRT(${asColumn(c)})`;
|
|
12475
12447
|
break;
|
|
12476
12448
|
case "utc":
|
|
12477
12449
|
case "time":
|
|
12478
12450
|
lift = (x2) => +x2;
|
|
12479
|
-
|
|
12451
|
+
sql2 = (c) => c instanceof Date ? +c : epoch_ms(asColumn(c));
|
|
12480
12452
|
break;
|
|
12481
12453
|
}
|
|
12482
|
-
return lift ? binFunction(domain, range2, lift,
|
|
12454
|
+
return lift ? binFunction(domain, range2, lift, sql2) : null;
|
|
12483
12455
|
}
|
|
12484
|
-
function binFunction(domain, range2, lift,
|
|
12456
|
+
function binFunction(domain, range2, lift, sql2) {
|
|
12485
12457
|
const lo = lift(Math.min(domain[0], domain[1]));
|
|
12486
12458
|
const hi = lift(Math.max(domain[0], domain[1]));
|
|
12487
12459
|
const a = Math.abs(lift(range2[1]) - lift(range2[0])) / (hi - lo);
|
|
12488
12460
|
return (value) => expr(
|
|
12489
|
-
`FLOOR(${a}::DOUBLE * (${
|
|
12461
|
+
`FLOOR(${a}::DOUBLE * (${sql2(value)} - ${lo}::DOUBLE))`,
|
|
12490
12462
|
asColumn(value).columns
|
|
12491
12463
|
);
|
|
12492
12464
|
}
|
|
@@ -12494,15 +12466,17 @@ async function createIndex(mc, table, query) {
|
|
|
12494
12466
|
try {
|
|
12495
12467
|
await mc.exec(`CREATE TEMP TABLE IF NOT EXISTS ${table} AS ${query}`);
|
|
12496
12468
|
} catch (err) {
|
|
12497
|
-
|
|
12469
|
+
mc.logger().error(err);
|
|
12498
12470
|
}
|
|
12499
12471
|
}
|
|
12472
|
+
var NO_INDEX = { from: NaN };
|
|
12500
12473
|
function getIndexColumns(client) {
|
|
12474
|
+
if (!client.filterIndexable)
|
|
12475
|
+
return NO_INDEX;
|
|
12501
12476
|
const q2 = client.query();
|
|
12502
12477
|
const from = getBaseTable(q2);
|
|
12503
|
-
if (!from || !q2.groupby
|
|
12504
|
-
return
|
|
12505
|
-
}
|
|
12478
|
+
if (!from || !q2.groupby)
|
|
12479
|
+
return NO_INDEX;
|
|
12506
12480
|
const g2 = new Set(q2.groupby().map((c) => c.column));
|
|
12507
12481
|
let aggr = [];
|
|
12508
12482
|
let dims = [];
|
|
@@ -12571,13 +12545,15 @@ function subqueryPushdown(query, cols) {
|
|
|
12571
12545
|
}
|
|
12572
12546
|
|
|
12573
12547
|
// src/util/throttle.js
|
|
12574
|
-
|
|
12548
|
+
var NIL = {};
|
|
12549
|
+
function throttle(callback, debounce = false) {
|
|
12575
12550
|
let curr;
|
|
12576
12551
|
let next;
|
|
12552
|
+
let pending = NIL;
|
|
12577
12553
|
function invoke(event) {
|
|
12578
12554
|
curr = callback(event).then(() => {
|
|
12579
12555
|
if (next) {
|
|
12580
|
-
const value = next;
|
|
12556
|
+
const { value } = next;
|
|
12581
12557
|
next = null;
|
|
12582
12558
|
invoke(value);
|
|
12583
12559
|
} else {
|
|
@@ -12586,18 +12562,31 @@ function throttle(callback) {
|
|
|
12586
12562
|
});
|
|
12587
12563
|
}
|
|
12588
12564
|
function enqueue(event) {
|
|
12589
|
-
next = event;
|
|
12565
|
+
next = { event };
|
|
12566
|
+
}
|
|
12567
|
+
function process(event) {
|
|
12568
|
+
curr ? enqueue(event) : invoke(event);
|
|
12569
|
+
}
|
|
12570
|
+
function delay(event) {
|
|
12571
|
+
if (pending !== event) {
|
|
12572
|
+
requestAnimationFrame(() => {
|
|
12573
|
+
const e = pending;
|
|
12574
|
+
pending = NIL;
|
|
12575
|
+
process(e);
|
|
12576
|
+
});
|
|
12577
|
+
}
|
|
12578
|
+
pending = event;
|
|
12590
12579
|
}
|
|
12591
|
-
return
|
|
12580
|
+
return debounce ? delay : process;
|
|
12592
12581
|
}
|
|
12593
12582
|
|
|
12594
12583
|
// src/FilterGroup.js
|
|
12595
12584
|
var FilterGroup = class {
|
|
12596
|
-
constructor(
|
|
12597
|
-
this.mc =
|
|
12585
|
+
constructor(coordinator2, selection, index = true) {
|
|
12586
|
+
this.mc = coordinator2;
|
|
12598
12587
|
this.selection = selection;
|
|
12599
12588
|
this.clients = /* @__PURE__ */ new Set();
|
|
12600
|
-
this.indexer = index ? new DataTileIndexer(mc, selection) : null;
|
|
12589
|
+
this.indexer = index ? new DataTileIndexer(this.mc, selection) : null;
|
|
12601
12590
|
const { value, activate } = this.handlers = {
|
|
12602
12591
|
value: throttle(() => this.update()),
|
|
12603
12592
|
activate: (clause) => {
|
|
@@ -12640,6 +12629,13 @@ function defaultUpdate(mc, clients, selection) {
|
|
|
12640
12629
|
}
|
|
12641
12630
|
|
|
12642
12631
|
// src/QueryCache.js
|
|
12632
|
+
var requestIdle = typeof requestIdleCallback !== "undefined" ? requestIdleCallback : setTimeout;
|
|
12633
|
+
var voidCache = () => ({
|
|
12634
|
+
get: () => void 0,
|
|
12635
|
+
set: (key, result) => result,
|
|
12636
|
+
clear: () => {
|
|
12637
|
+
}
|
|
12638
|
+
});
|
|
12643
12639
|
var QueryCache = class {
|
|
12644
12640
|
constructor({
|
|
12645
12641
|
max: max2 = 1e3,
|
|
@@ -12663,16 +12659,10 @@ var QueryCache = class {
|
|
|
12663
12659
|
}
|
|
12664
12660
|
set(key, promise) {
|
|
12665
12661
|
const { cache, max: max2 } = this;
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
});
|
|
12671
|
-
cache.set(key, { last: now, promise });
|
|
12672
|
-
if (cache.size > max2) {
|
|
12673
|
-
setTimeout(() => this.evict());
|
|
12674
|
-
}
|
|
12675
|
-
return receive;
|
|
12662
|
+
cache.set(key, { last: performance.now(), promise });
|
|
12663
|
+
if (cache.size > max2)
|
|
12664
|
+
requestIdle(() => this.evict());
|
|
12665
|
+
return promise;
|
|
12676
12666
|
}
|
|
12677
12667
|
evict() {
|
|
12678
12668
|
const expire = performance.now() - this.ttl;
|
|
@@ -12694,6 +12684,22 @@ var QueryCache = class {
|
|
|
12694
12684
|
}
|
|
12695
12685
|
};
|
|
12696
12686
|
|
|
12687
|
+
// src/util/void-logger.js
|
|
12688
|
+
function voidLogger() {
|
|
12689
|
+
return {
|
|
12690
|
+
debug() {
|
|
12691
|
+
},
|
|
12692
|
+
info() {
|
|
12693
|
+
},
|
|
12694
|
+
log() {
|
|
12695
|
+
},
|
|
12696
|
+
warn() {
|
|
12697
|
+
},
|
|
12698
|
+
error() {
|
|
12699
|
+
}
|
|
12700
|
+
};
|
|
12701
|
+
}
|
|
12702
|
+
|
|
12697
12703
|
// src/Coordinator.js
|
|
12698
12704
|
var _instance;
|
|
12699
12705
|
function coordinator(instance17) {
|
|
@@ -12705,27 +12711,25 @@ function coordinator(instance17) {
|
|
|
12705
12711
|
return _instance;
|
|
12706
12712
|
}
|
|
12707
12713
|
var Coordinator = class {
|
|
12708
|
-
constructor(db = socketClient()) {
|
|
12709
|
-
this.cache = new QueryCache();
|
|
12714
|
+
constructor(db = socketClient(), options = {}) {
|
|
12710
12715
|
this.catalog = new Catalog(this);
|
|
12711
|
-
this.
|
|
12716
|
+
this.logger(options.logger || console);
|
|
12717
|
+
this.configure(options);
|
|
12712
12718
|
this.databaseClient(db);
|
|
12713
12719
|
this.clear();
|
|
12714
12720
|
}
|
|
12721
|
+
logger(logger) {
|
|
12722
|
+
return arguments.length ? this._logger = logger || voidLogger() : this._logger;
|
|
12723
|
+
}
|
|
12715
12724
|
configure({ cache = true, indexes = true }) {
|
|
12716
|
-
this.cache = cache ? new QueryCache() :
|
|
12717
|
-
get: () => void 0,
|
|
12718
|
-
set: (key, result) => result,
|
|
12719
|
-
clear: () => {
|
|
12720
|
-
}
|
|
12721
|
-
};
|
|
12725
|
+
this.cache = cache ? new QueryCache() : voidCache();
|
|
12722
12726
|
this.indexes = indexes;
|
|
12723
12727
|
}
|
|
12724
12728
|
clear({ clients = true, cache = true, catalog = false } = {}) {
|
|
12725
12729
|
if (clients) {
|
|
12726
|
-
this.clients?.forEach((
|
|
12730
|
+
this.clients?.forEach((client) => this.disconnect(client));
|
|
12727
12731
|
this.filterGroups?.forEach((group) => group.finalize());
|
|
12728
|
-
this.clients = /* @__PURE__ */ new
|
|
12732
|
+
this.clients = /* @__PURE__ */ new Set();
|
|
12729
12733
|
this.filterGroups = /* @__PURE__ */ new Map();
|
|
12730
12734
|
}
|
|
12731
12735
|
if (cache)
|
|
@@ -12739,21 +12743,25 @@ var Coordinator = class {
|
|
|
12739
12743
|
}
|
|
12740
12744
|
return this.db;
|
|
12741
12745
|
}
|
|
12742
|
-
async exec(
|
|
12746
|
+
async exec(sql2) {
|
|
12743
12747
|
try {
|
|
12744
|
-
await this.db.query({ type: "exec", sql });
|
|
12748
|
+
await this.db.query({ type: "exec", sql: sql2 });
|
|
12745
12749
|
} catch (err) {
|
|
12746
|
-
|
|
12750
|
+
this._logger.error(err);
|
|
12747
12751
|
}
|
|
12748
12752
|
}
|
|
12749
|
-
|
|
12750
|
-
const
|
|
12751
|
-
const
|
|
12753
|
+
query(query, { type = "arrow", cache = true } = {}) {
|
|
12754
|
+
const sql2 = String(query);
|
|
12755
|
+
const t0 = performance.now();
|
|
12756
|
+
const cached = this.cache.get(sql2);
|
|
12752
12757
|
if (cached) {
|
|
12758
|
+
this._logger.debug("Cache");
|
|
12753
12759
|
return cached;
|
|
12754
12760
|
} else {
|
|
12755
|
-
const request = this.db.query({ type, sql });
|
|
12756
|
-
|
|
12761
|
+
const request = this.db.query({ type, sql: sql2 });
|
|
12762
|
+
const result = cache ? this.cache.set(sql2, request) : request;
|
|
12763
|
+
result.then(() => this._logger.debug(`Query: ${performance.now() - t0}`));
|
|
12764
|
+
return result;
|
|
12757
12765
|
}
|
|
12758
12766
|
}
|
|
12759
12767
|
async updateClient(client, query) {
|
|
@@ -12762,22 +12770,26 @@ var Coordinator = class {
|
|
|
12762
12770
|
client.queryPending();
|
|
12763
12771
|
result = await this.query(query);
|
|
12764
12772
|
} catch (err) {
|
|
12765
|
-
|
|
12773
|
+
this._logger.error(err);
|
|
12766
12774
|
client.queryError(err);
|
|
12767
12775
|
return;
|
|
12768
12776
|
}
|
|
12769
12777
|
try {
|
|
12770
12778
|
client.queryResult(result).update();
|
|
12771
12779
|
} catch (err) {
|
|
12772
|
-
|
|
12780
|
+
this._logger.error(err);
|
|
12773
12781
|
}
|
|
12774
12782
|
}
|
|
12783
|
+
async requestQuery(client, query) {
|
|
12784
|
+
this.filterGroups.get(client.filterBy)?.reset();
|
|
12785
|
+
return query ? this.updateClient(client, query) : client.update();
|
|
12786
|
+
}
|
|
12775
12787
|
async connect(client) {
|
|
12776
12788
|
const { catalog, clients, filterGroups, indexes } = this;
|
|
12777
12789
|
if (clients.has(client)) {
|
|
12778
12790
|
throw new Error("Client already connected.");
|
|
12779
12791
|
}
|
|
12780
|
-
clients.
|
|
12792
|
+
clients.add(client);
|
|
12781
12793
|
const fields = client.fields();
|
|
12782
12794
|
if (fields?.length) {
|
|
12783
12795
|
client.fieldStats(await catalog.queryFields(fields));
|
|
@@ -12791,41 +12803,139 @@ var Coordinator = class {
|
|
|
12791
12803
|
filterGroups.set(filter, group.add(client));
|
|
12792
12804
|
}
|
|
12793
12805
|
}
|
|
12794
|
-
|
|
12795
|
-
const q2 = query || client.query(filter?.predicate(client));
|
|
12796
|
-
filterGroups.get(filter)?.reset();
|
|
12797
|
-
if (q2)
|
|
12798
|
-
this.updateClient(client, q2);
|
|
12799
|
-
};
|
|
12800
|
-
clients.set(client, handler);
|
|
12801
|
-
client.request?.addEventListener("value", handler);
|
|
12802
|
-
handler();
|
|
12806
|
+
client.requestQuery();
|
|
12803
12807
|
}
|
|
12804
12808
|
disconnect(client) {
|
|
12805
12809
|
const { clients, filterGroups } = this;
|
|
12806
12810
|
if (!clients.has(client))
|
|
12807
12811
|
return;
|
|
12808
|
-
const handler = clients.get(client);
|
|
12809
12812
|
clients.delete(client);
|
|
12810
12813
|
filterGroups.get(client.filterBy)?.remove(client);
|
|
12811
|
-
client.request?.removeEventListener(handler);
|
|
12812
12814
|
}
|
|
12813
12815
|
};
|
|
12814
12816
|
|
|
12815
|
-
// src/
|
|
12816
|
-
|
|
12817
|
-
|
|
12817
|
+
// src/MosaicClient.js
|
|
12818
|
+
var MosaicClient = class {
|
|
12819
|
+
/**
|
|
12820
|
+
* Constructor.
|
|
12821
|
+
* @param {*} filterSelection An optional selection to interactively filter
|
|
12822
|
+
* this client's data. If provided, a coordinator will re-query and update
|
|
12823
|
+
* the client when the selection updates.
|
|
12824
|
+
*/
|
|
12825
|
+
constructor(filterSelection) {
|
|
12826
|
+
this._filterBy = filterSelection;
|
|
12827
|
+
this._requestUpdate = throttle(() => this.requestQuery(), true);
|
|
12828
|
+
}
|
|
12829
|
+
/**
|
|
12830
|
+
* Return this client's filter selection.
|
|
12831
|
+
*/
|
|
12832
|
+
get filterBy() {
|
|
12833
|
+
return this._filterBy;
|
|
12834
|
+
}
|
|
12835
|
+
/**
|
|
12836
|
+
* Return a boolean indicating if the client query can be indexed. Should
|
|
12837
|
+
* return true if changes to the filterBy selection does not change the
|
|
12838
|
+
* groupby domain of the client query.
|
|
12839
|
+
*/
|
|
12840
|
+
get filterIndexable() {
|
|
12841
|
+
return true;
|
|
12842
|
+
}
|
|
12843
|
+
/**
|
|
12844
|
+
* Return an array of fields queried by this client.
|
|
12845
|
+
*/
|
|
12846
|
+
fields() {
|
|
12847
|
+
return null;
|
|
12848
|
+
}
|
|
12849
|
+
/**
|
|
12850
|
+
* Called by the coordinator to set the field statistics for this client.
|
|
12851
|
+
* @returns {this}
|
|
12852
|
+
*/
|
|
12853
|
+
fieldStats() {
|
|
12854
|
+
return this;
|
|
12855
|
+
}
|
|
12856
|
+
/**
|
|
12857
|
+
* Return a query specifying the data needed by this client.
|
|
12858
|
+
*/
|
|
12859
|
+
query() {
|
|
12860
|
+
return null;
|
|
12861
|
+
}
|
|
12862
|
+
/**
|
|
12863
|
+
* Called by the coordinator to inform the client that a query is pending.
|
|
12864
|
+
*/
|
|
12865
|
+
queryPending() {
|
|
12866
|
+
return this;
|
|
12867
|
+
}
|
|
12868
|
+
/**
|
|
12869
|
+
* Called by the coordinator to return a query result.
|
|
12870
|
+
*/
|
|
12871
|
+
queryResult() {
|
|
12872
|
+
return this;
|
|
12873
|
+
}
|
|
12874
|
+
/**
|
|
12875
|
+
* Called by the coordinator to report a query execution error.
|
|
12876
|
+
*/
|
|
12877
|
+
queryError(error) {
|
|
12878
|
+
console.error(error);
|
|
12879
|
+
return this;
|
|
12880
|
+
}
|
|
12881
|
+
/**
|
|
12882
|
+
* Request the coordinator to execute a query for this client.
|
|
12883
|
+
* If an explicit query is not provided, the client query method will
|
|
12884
|
+
* be called, filtered by the current filterBy selection.
|
|
12885
|
+
*/
|
|
12886
|
+
requestQuery(query) {
|
|
12887
|
+
const q2 = query || this.query(this.filterBy?.predicate(this));
|
|
12888
|
+
return coordinator().requestQuery(this, q2);
|
|
12889
|
+
}
|
|
12890
|
+
/**
|
|
12891
|
+
* Request that the coordinator perform a throttled update of this client
|
|
12892
|
+
* using the default query. Unlike requestQuery, for which every call will
|
|
12893
|
+
* result in an executed query, multiple calls to requestUpdate may be
|
|
12894
|
+
* consolidated into a single update.
|
|
12895
|
+
*/
|
|
12896
|
+
requestUpdate() {
|
|
12897
|
+
this._requestUpdate();
|
|
12898
|
+
}
|
|
12899
|
+
/**
|
|
12900
|
+
* Requests a client update.
|
|
12901
|
+
* For example to (re-)render an interface component.
|
|
12902
|
+
*/
|
|
12903
|
+
update() {
|
|
12904
|
+
return this;
|
|
12905
|
+
}
|
|
12906
|
+
};
|
|
12907
|
+
|
|
12908
|
+
// src/util/distinct.js
|
|
12909
|
+
function distinct(a, b2) {
|
|
12910
|
+
return a === b2 ? false : a instanceof Date && b2 instanceof Date ? +a !== +b2 : Array.isArray(a) && Array.isArray(b2) ? distinctArray(a, b2) : true;
|
|
12911
|
+
}
|
|
12912
|
+
function distinctArray(a, b2) {
|
|
12913
|
+
if (a.length !== b2.length)
|
|
12914
|
+
return true;
|
|
12915
|
+
for (let i2 = 0; i2 < a.length; ++i2) {
|
|
12916
|
+
if (a[i2] !== b2[i2])
|
|
12917
|
+
return true;
|
|
12918
|
+
}
|
|
12919
|
+
return false;
|
|
12920
|
+
}
|
|
12921
|
+
|
|
12922
|
+
// src/Param.js
|
|
12923
|
+
function isParam(x2) {
|
|
12924
|
+
return x2 instanceof Param;
|
|
12818
12925
|
}
|
|
12819
|
-
var
|
|
12926
|
+
var Param = class {
|
|
12820
12927
|
constructor(value) {
|
|
12821
12928
|
this._value = value;
|
|
12822
12929
|
this._listeners = /* @__PURE__ */ new Map();
|
|
12823
12930
|
}
|
|
12931
|
+
static value(value) {
|
|
12932
|
+
return new Param(value);
|
|
12933
|
+
}
|
|
12824
12934
|
get value() {
|
|
12825
12935
|
return this._value;
|
|
12826
12936
|
}
|
|
12827
12937
|
update(value, { force } = {}) {
|
|
12828
|
-
const changed = this._value
|
|
12938
|
+
const changed = distinct(this._value, value);
|
|
12829
12939
|
if (changed)
|
|
12830
12940
|
this._value = value;
|
|
12831
12941
|
if (changed || force)
|
|
@@ -12834,7 +12944,7 @@ var Signal = class {
|
|
|
12834
12944
|
}
|
|
12835
12945
|
addEventListener(type, callback) {
|
|
12836
12946
|
let list = this._listeners.get(type) || [];
|
|
12837
|
-
if (list.
|
|
12947
|
+
if (!list.includes(callback)) {
|
|
12838
12948
|
list = list.concat(callback);
|
|
12839
12949
|
}
|
|
12840
12950
|
this._listeners.set(type, list);
|
|
@@ -12854,7 +12964,7 @@ var Signal = class {
|
|
|
12854
12964
|
function isSelection(x2) {
|
|
12855
12965
|
return x2 instanceof Selection;
|
|
12856
12966
|
}
|
|
12857
|
-
var Selection = class extends
|
|
12967
|
+
var Selection = class extends Param {
|
|
12858
12968
|
static intersect() {
|
|
12859
12969
|
return new Selection();
|
|
12860
12970
|
}
|
|
@@ -24029,8 +24139,8 @@ async function wasmClient(options) {
|
|
|
24029
24139
|
db,
|
|
24030
24140
|
con,
|
|
24031
24141
|
query: async (query) => {
|
|
24032
|
-
const { type, sql } = query;
|
|
24033
|
-
const result = await con.query(
|
|
24142
|
+
const { type, sql: sql2 } = query;
|
|
24143
|
+
const result = await con.query(sql2);
|
|
24034
24144
|
return type === "exec" ? void 0 : type === "arrow" ? result : Array.from(result);
|
|
24035
24145
|
}
|
|
24036
24146
|
};
|
|
@@ -24053,11 +24163,12 @@ async function initDatabase({
|
|
|
24053
24163
|
export {
|
|
24054
24164
|
Coordinator,
|
|
24055
24165
|
MosaicClient,
|
|
24166
|
+
Param,
|
|
24056
24167
|
Selection,
|
|
24057
|
-
Signal,
|
|
24058
24168
|
coordinator,
|
|
24169
|
+
distinct,
|
|
24170
|
+
isParam,
|
|
24059
24171
|
isSelection,
|
|
24060
|
-
isSignal,
|
|
24061
24172
|
restClient,
|
|
24062
24173
|
socketClient,
|
|
24063
24174
|
sqlFrom,
|