@gscdump/engine-duckdb-wasm 0.36.1 → 0.36.3
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/dist/index.mjs +29 -13
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -24,11 +24,18 @@ const DIM_COLUMN = {
|
|
|
24
24
|
date: "date",
|
|
25
25
|
searchAppearance: "search_appearance"
|
|
26
26
|
};
|
|
27
|
-
function
|
|
27
|
+
function quoteIdent(value) {
|
|
28
|
+
return `"${value.replace(/"/g, "\"\"")}"`;
|
|
29
|
+
}
|
|
30
|
+
function qualifiedColumn(table, column) {
|
|
31
|
+
return `${quoteIdent(table)}.${quoteIdent(column)}`;
|
|
32
|
+
}
|
|
33
|
+
function queryCanonicalExpr(table) {
|
|
34
|
+
const queryRef = qualifiedColumn(table, "query");
|
|
28
35
|
return `COALESCE((SELECT qd.query_canonical FROM query_dim qd WHERE qd.query = ${queryRef} LIMIT 1), ${queryRef})`;
|
|
29
36
|
}
|
|
30
|
-
function dimExpr(dim) {
|
|
31
|
-
return dim === "queryCanonical" ? queryCanonicalExpr() : DIM_COLUMN[dim] ?? dim;
|
|
37
|
+
function dimExpr(dim, table) {
|
|
38
|
+
return dim === "queryCanonical" ? queryCanonicalExpr(table) : DIM_COLUMN[dim] ?? dim;
|
|
32
39
|
}
|
|
33
40
|
function tableForDimensions(dims) {
|
|
34
41
|
const set = new Set(dims.filter((d) => d !== "date"));
|
|
@@ -38,6 +45,15 @@ function tableForDimensions(dims) {
|
|
|
38
45
|
if (set.has("device")) return "dates";
|
|
39
46
|
return "pages";
|
|
40
47
|
}
|
|
48
|
+
function tableForTopNBreakdown(query) {
|
|
49
|
+
const dims = [query.dimension];
|
|
50
|
+
for (const facet of query.facets ?? []) {
|
|
51
|
+
const hasPage = query.dimension === "page" || facet.column === "page";
|
|
52
|
+
const hasQuery = query.dimension === "query" || query.dimension === "queryCanonical" || facet.column === "query" || facet.column === "queryCanonical";
|
|
53
|
+
if (hasPage && hasQuery) dims.push(facet.column);
|
|
54
|
+
}
|
|
55
|
+
return tableForDimensions(dims);
|
|
56
|
+
}
|
|
41
57
|
function metricExpr(metric) {
|
|
42
58
|
const expr = METRIC_SQL[metric];
|
|
43
59
|
if (!expr) throw new Error(`[archetype-sql] unknown metric: ${metric}`);
|
|
@@ -124,7 +140,7 @@ function moverClause(movers) {
|
|
|
124
140
|
default: throw new Error(`[archetype-sql] unknown movers mode: ${movers}`);
|
|
125
141
|
}
|
|
126
142
|
}
|
|
127
|
-
function facetPredicate(query) {
|
|
143
|
+
function facetPredicate(query, table) {
|
|
128
144
|
const facets = query.facets;
|
|
129
145
|
if (!facets?.length) return {
|
|
130
146
|
sql: "",
|
|
@@ -133,7 +149,7 @@ function facetPredicate(query) {
|
|
|
133
149
|
const parts = [];
|
|
134
150
|
const params = [];
|
|
135
151
|
for (const f of facets) {
|
|
136
|
-
const col = dimExpr(f.column);
|
|
152
|
+
const col = dimExpr(f.column, table);
|
|
137
153
|
switch (f.op) {
|
|
138
154
|
case "eq":
|
|
139
155
|
parts.push(`${col} = ?`);
|
|
@@ -168,7 +184,7 @@ function compileArchetypeSql(query) {
|
|
|
168
184
|
}
|
|
169
185
|
case "entity-daily-timeseries": {
|
|
170
186
|
const table = tableForDimensions([query.entity.dimension]);
|
|
171
|
-
const col = dimExpr(query.entity.dimension);
|
|
187
|
+
const col = dimExpr(query.entity.dimension, table);
|
|
172
188
|
const where = rangePredicate(query);
|
|
173
189
|
return {
|
|
174
190
|
table,
|
|
@@ -178,7 +194,7 @@ function compileArchetypeSql(query) {
|
|
|
178
194
|
}
|
|
179
195
|
case "entity-daily-sparkline": {
|
|
180
196
|
const table = tableForDimensions([query.dimension]);
|
|
181
|
-
const col = dimExpr(query.dimension);
|
|
197
|
+
const col = dimExpr(query.dimension, table);
|
|
182
198
|
const where = rangePredicate(query);
|
|
183
199
|
if (query.entities.length === 0) throw new Error("[archetype-sql] entity-daily-sparkline requires resolved entities");
|
|
184
200
|
const placeholders = query.entities.map(() => "?").join(", ");
|
|
@@ -189,7 +205,7 @@ function compileArchetypeSql(query) {
|
|
|
189
205
|
};
|
|
190
206
|
}
|
|
191
207
|
case "top-n-breakdown": {
|
|
192
|
-
const table =
|
|
208
|
+
const table = tableForTopNBreakdown(query);
|
|
193
209
|
const where = rangePredicate(query);
|
|
194
210
|
const cmp = compareRangePredicate(query);
|
|
195
211
|
const dir = query.orderBy.dir === "asc" ? "ASC" : "DESC";
|
|
@@ -237,8 +253,8 @@ function compileArchetypeSql(query) {
|
|
|
237
253
|
params
|
|
238
254
|
};
|
|
239
255
|
}
|
|
240
|
-
const col = dimExpr(query.dimension);
|
|
241
|
-
const facet = facetPredicate(query);
|
|
256
|
+
const col = dimExpr(query.dimension, table);
|
|
257
|
+
const facet = facetPredicate(query, table);
|
|
242
258
|
const totalCol = query.includeTotal ? ", COUNT(*) OVER() AS __total" : "";
|
|
243
259
|
if (cmp) {
|
|
244
260
|
const curCols = metricList.map((m) => coalesceMetric(m, "c", m)).join(", ");
|
|
@@ -287,7 +303,7 @@ function compileArchetypeSql(query) {
|
|
|
287
303
|
const matchParts = [];
|
|
288
304
|
const matchParams = [];
|
|
289
305
|
for (const [dim, value] of Object.entries(query.match)) {
|
|
290
|
-
const col = dimExpr(dim);
|
|
306
|
+
const col = dimExpr(dim, table);
|
|
291
307
|
if (!col) throw new Error(`[archetype-sql] single-row-lookup: unknown dimension ${dim}`);
|
|
292
308
|
matchParts.push(`${col} = ?`);
|
|
293
309
|
matchParams.push(value);
|
|
@@ -311,7 +327,7 @@ function compileArchetypeSql(query) {
|
|
|
311
327
|
...where.params
|
|
312
328
|
]
|
|
313
329
|
};
|
|
314
|
-
const col = dimExpr(query.seriesDimension);
|
|
330
|
+
const col = dimExpr(query.seriesDimension, table);
|
|
315
331
|
return {
|
|
316
332
|
table,
|
|
317
333
|
sql: `SELECT date, ${col} AS ${query.seriesDimension}, ${metricExpr(query.metric)} AS ${query.metric} FROM ${table} WHERE ${where.sql} GROUP BY date, ${col} ORDER BY date, ${col}`,
|
|
@@ -321,7 +337,7 @@ function compileArchetypeSql(query) {
|
|
|
321
337
|
case "two-dimension-detail": {
|
|
322
338
|
const table = "page_queries";
|
|
323
339
|
const where = rangePredicate(query);
|
|
324
|
-
const facet = facetPredicate(query);
|
|
340
|
+
const facet = facetPredicate(query, table);
|
|
325
341
|
const filterParts = [];
|
|
326
342
|
const filterParams = [];
|
|
327
343
|
if (query.filter?.page) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/engine-duckdb-wasm",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.36.
|
|
4
|
+
"version": "0.36.3",
|
|
5
5
|
"description": "DuckDB-WASM engine adapter for @gscdump/analysis — typed browser analytics against parquet via R2.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"drizzle-orm": "1.0.0-rc.3",
|
|
48
|
-
"@gscdump/contracts": "0.36.
|
|
49
|
-
"@gscdump/engine": "0.36.
|
|
50
|
-
"gscdump": "0.36.
|
|
48
|
+
"@gscdump/contracts": "0.36.3",
|
|
49
|
+
"@gscdump/engine": "0.36.3",
|
|
50
|
+
"gscdump": "0.36.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@duckdb/duckdb-wasm": "^1.32.0",
|