@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.
Files changed (2) hide show
  1. package/dist/index.mjs +29 -13
  2. 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 queryCanonicalExpr(queryRef = "query") {
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 = tableForDimensions([query.dimension]);
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.1",
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.1",
49
- "@gscdump/engine": "0.36.1",
50
- "gscdump": "0.36.1"
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",