@gscdump/engine 0.35.1 → 0.35.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.
@@ -282,7 +282,6 @@ function createSqlFragments(config) {
282
282
  let col;
283
283
  if (metric === "clicks") col = t.clicks;
284
284
  else if (metric === "impressions") col = t.impressions;
285
- else if (metric === "position") col = t.sum_position;
286
285
  if (!col) continue;
287
286
  const v = Number(f.expression);
288
287
  switch (f.operator) {
@@ -1,5 +1,5 @@
1
- import { SCHEMAS } from "./schema.mjs";
2
- import { DEFAULT_PARTITION_KEY_ENCODING, DEFAULT_PARTITION_KEY_ENCODING as DEFAULT_PARTITION_KEY_ENCODING$1 } from "@gscdump/lakehouse";
1
+ import { SCHEMAS, TABLE_METADATA } from "./schema.mjs";
2
+ import { DEFAULT_PARTITION_KEY_ENCODING, DEFAULT_PARTITION_KEY_ENCODING as DEFAULT_PARTITION_KEY_ENCODING$1, defineIcebergDataset } from "@gscdump/lakehouse";
3
3
  function mapColumnType(t) {
4
4
  switch (t) {
5
5
  case "VARCHAR": return "STRING";
@@ -72,27 +72,43 @@ const ICEBERG_PARTITION_SPEC = [
72
72
  name: "date_month"
73
73
  }
74
74
  ];
75
- function icebergTableSpec(table, encoding = DEFAULT_PARTITION_KEY_ENCODING) {
76
- const base = SCHEMAS[table];
77
- const dataColumns = base.columns.map((col, i) => ({
75
+ function dataColumnsFor(table) {
76
+ return SCHEMAS[table].columns.map((col) => ({
78
77
  name: col.name,
79
78
  type: mapColumnType(col.type),
80
- required: !col.nullable,
81
- fieldId: 3 + i
79
+ required: !col.nullable
82
80
  }));
81
+ }
82
+ function searchTypeDim(encoding) {
83
+ return {
84
+ toPartitionValue: (v) => encoding === "int" ? SEARCH_TYPE_INT[v] : v,
85
+ boundEncoding: encoding === "int" ? "int32" : "string"
86
+ };
87
+ }
88
+ function gscDatasetDef(table, encoding) {
89
+ const meta = TABLE_METADATA[table];
83
90
  return {
84
91
  namespace: "gsc",
85
92
  table,
86
- columns: [...icebergPartitionColumns(encoding), ...dataColumns],
87
- partitionSpec: ICEBERG_PARTITION_SPEC,
88
- naturalKey: base.sortKey,
89
- identityColumns: [
90
- "site_id",
91
- "search_type",
92
- ...base.sortKey
93
- ]
93
+ identity: {
94
+ kind: "site-int",
95
+ encoding
96
+ },
97
+ dims: { search_type: searchTypeDim(encoding) },
98
+ columns: dataColumnsFor(table),
99
+ partition: ICEBERG_PARTITION_SPEC,
100
+ naturalKey: meta.sortKey,
101
+ clusterKey: meta.clusterKey
94
102
  };
95
103
  }
104
+ const GSC_DATASETS_STRING = Object.fromEntries(ICEBERG_TABLES.map((t) => [t, defineIcebergDataset(gscDatasetDef(t, "string"))]));
105
+ const GSC_DATASETS_INT = Object.fromEntries(ICEBERG_TABLES.map((t) => [t, defineIcebergDataset(gscDatasetDef(t, "int"))]));
106
+ function gscDataset(table, encoding = DEFAULT_PARTITION_KEY_ENCODING) {
107
+ return (encoding === "int" ? GSC_DATASETS_INT : GSC_DATASETS_STRING)[table];
108
+ }
109
+ function icebergTableSpec(table, encoding = DEFAULT_PARTITION_KEY_ENCODING) {
110
+ return gscDataset(table, encoding).tableSpec;
111
+ }
96
112
  const ICEBERG_SCHEMAS_STRING = Object.fromEntries(ICEBERG_TABLES.map((t) => [t, icebergTableSpec(t, "string")]));
97
113
  const ICEBERG_SCHEMAS_INT = Object.fromEntries(ICEBERG_TABLES.map((t) => [t, icebergTableSpec(t, "int")]));
98
114
  const ICEBERG_SCHEMAS = ICEBERG_SCHEMAS_INT;
@@ -107,4 +123,4 @@ function assertIcebergTable(table) {
107
123
  if (!isIcebergTable(table)) throw new Error(`Unknown Iceberg table '${table}'. Expected one of: ${ICEBERG_TABLES.join(", ")}`);
108
124
  return table;
109
125
  }
110
- export { DEFAULT_PARTITION_KEY_ENCODING$1 as DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable };
126
+ export { DEFAULT_PARTITION_KEY_ENCODING$1 as DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, gscDataset, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable };
@@ -34,15 +34,18 @@ declare function icebergPartitionColumns(encoding?: PartitionKeyEncoding): reado
34
34
  /**
35
35
  * First field id used for per-table (non-partition) columns — immediately
36
36
  * after the two partition-identity columns (`site_id`=1, `search_type`=2).
37
+ * No longer read by `defineDataset` below (field ids are now assigned by
38
+ * `defineIcebergDataset` itself, identity-then-dims-then-columns) — kept as a
39
+ * documented, byte-identity-tested constant for existing external readers.
37
40
  */
38
41
  declare const ICEBERG_FIELD_ID_BASE = 3;
39
42
  /** Shared partition spec — identical across every table. */
40
43
  declare const ICEBERG_PARTITION_SPEC: readonly IcebergPartitionField[];
41
44
  /**
42
- * Derive the full Iceberg table spec for a table from the engine `SCHEMAS`
43
- * (drizzle-derived column set) plus the shared partition-identity columns.
44
- * Field ids are assigned deterministically from `ICEBERG_FIELD_ID_BASE` in
45
- * column declaration order so the same schema always yields the same ids.
45
+ * Derive the full Iceberg table spec for a table + encoding. Thin wrapper
46
+ * over {@link gscDataset}'s `tableSpec` (ADR-0021 R2-FIXES C5) kept as a
47
+ * named export so existing `@gscdump/engine/iceberg` consumers see no change
48
+ * to their import surface.
46
49
  *
47
50
  * CONTRACT NOTE: implementation agents must treat the RETURNED VALUE as the
48
51
  * source of truth — do not hand-list columns elsewhere.
@@ -1,6 +1,6 @@
1
1
  import { TABLE_METADATA } from "../_chunks/schema.mjs";
2
2
  import { engineErrors } from "../errors.mjs";
3
- import { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable } from "../_chunks/schema2.mjs";
3
+ import { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, gscDataset, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable } from "../_chunks/schema2.mjs";
4
4
  import { icebergCreateTable, icebergManifests, restCatalogLoadTable } from "../_chunks/libs/icebird.mjs";
5
5
  import { err, ok } from "gscdump/result";
6
6
  import { ICEBERG_TYPE_MAP, connectIcebergCatalog, dropIcebergTables as dropIcebergTables$1, ensureIcebergNamespace, listIcebergTables, resolveIcebergDataFiles } from "@gscdump/lakehouse";
@@ -126,7 +126,7 @@ function toIntPartitionSiteId(value) {
126
126
  }
127
127
  function dedupeByIdentity(table, records) {
128
128
  if (records.length < 2) return records;
129
- const key = ICEBERG_SCHEMAS[table].identityColumns;
129
+ const key = gscDataset(table, "int").tableSpec.identityColumns;
130
130
  const seen = /* @__PURE__ */ new Map();
131
131
  for (const rec of records) {
132
132
  const k = key.map((col) => `${rec[col] ?? ""}`).join("\0");
@@ -135,7 +135,7 @@ function dedupeByIdentity(table, records) {
135
135
  return seen.size === records.length ? records : [...seen.values()];
136
136
  }
137
137
  function sortByClusterKey(table, records) {
138
- const cols = TABLE_METADATA[table].clusterKey;
138
+ const cols = gscDataset(table, "int").tableSpec.clusterKey ?? [];
139
139
  if (cols.length === 0 || records.length < 2) return records;
140
140
  return records.slice().sort((a, b) => {
141
141
  for (const col of cols) {
@@ -154,7 +154,7 @@ function sortByClusterKey(table, records) {
154
154
  }
155
155
  function toRecords(slice, rows, encoding) {
156
156
  const siteVal = encoding === "int" ? toIntPartitionSiteId(slice.ctx.siteId) : slice.ctx.siteId ?? "";
157
- const searchVal = encoding === "int" ? SEARCH_TYPE_INT[slice.searchType] : slice.searchType;
157
+ const searchVal = gscDataset(slice.table, encoding).def.dims.search_type.toPartitionValue(slice.searchType);
158
158
  return rows.map((row) => {
159
159
  const out = {};
160
160
  for (const k in row) out[k] = coerceJsonSafe(row[k]);
package/dist/ingest.mjs CHANGED
@@ -26,7 +26,7 @@ function toPath(gscUrl) {
26
26
  }
27
27
  }
28
28
  function toSumPosition(apiPosition, impressions) {
29
- return ((apiPosition >= 1 ? apiPosition : 1) - 1) * Math.max(impressions, 1);
29
+ return ((apiPosition >= 1 ? apiPosition : 1) - 1) * impressions;
30
30
  }
31
31
  function transformGscRow(table, apiRow, options = {}) {
32
32
  const keys = apiRow.keys;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/engine",
3
3
  "type": "module",
4
- "version": "0.35.1",
4
+ "version": "0.35.3",
5
5
  "description": "Append-only Parquet/DuckDB storage engine + planner + adapters for the gscdump pipeline. Node + edge runtimes; opt-in heavy peers.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -181,9 +181,9 @@
181
181
  "dependencies": {
182
182
  "drizzle-orm": "1.0.0-rc.3",
183
183
  "proper-lockfile": "^4.1.2",
184
- "@gscdump/lakehouse": "0.35.1",
185
- "gscdump": "0.35.1",
186
- "@gscdump/contracts": "0.35.1"
184
+ "@gscdump/contracts": "0.35.3",
185
+ "@gscdump/lakehouse": "0.35.3",
186
+ "gscdump": "0.35.3"
187
187
  },
188
188
  "devDependencies": {
189
189
  "@duckdb/duckdb-wasm": "^1.32.0",