@gscdump/engine 0.32.11 → 0.33.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/dist/_chunks/engine.mjs +45 -22
- package/dist/_chunks/pg-adapter.d.mts +30 -4
- package/dist/_chunks/resolver.mjs +201 -37
- package/dist/_chunks/schema.d.mts +0 -120
- package/dist/_chunks/schema.mjs +4 -8
- package/dist/_chunks/schema2.mjs +8 -6
- package/dist/_chunks/sink.d.mts +18 -12
- package/dist/_chunks/types.d.mts +3 -0
- package/dist/adapters/hyparquet.mjs +7 -4
- package/dist/adapters/r2-manifest.mjs +40 -29
- package/dist/entities.d.mts +2 -2
- package/dist/iceberg/index.d.mts +2 -2
- package/dist/iceberg/index.mjs +9 -9
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/ingest.d.mts +1 -6
- package/dist/ingest.mjs +0 -7
- package/dist/resolver/index.d.mts +91 -36
- package/dist/resolver/index.mjs +2 -2
- package/dist/rollups.d.mts +4 -4
- package/dist/rollups.mjs +28 -19
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -6,7 +6,7 @@ import { InspectionVerdict, SchedulePolicy, ScheduleState, fixedPolicy, inspecti
|
|
|
6
6
|
import { IcebergTableName, Sink, SinkCapabilities, SinkCloseResult, SinkOptions, SinkSlice, SinkWriteResult } from "./_chunks/sink.mjs";
|
|
7
7
|
import { GscApiRow, IngestOptions, RowAccumulator, RowAccumulatorOptions, assembleDatesRow, createRowAccumulator, toPath, toSumPosition, transformGscRow } from "./ingest.mjs";
|
|
8
8
|
import { FILES_PLACEHOLDER, ResolvedQuery, resolveParquetSQL, substituteNamedFiles } from "./_chunks/planner.mjs";
|
|
9
|
-
import { createIcebergResolverAdapter, createParquetResolverAdapter, pgResolverAdapter } from "./_chunks/pg-adapter.mjs";
|
|
9
|
+
import { createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, pgResolverAdapter } from "./_chunks/pg-adapter.mjs";
|
|
10
10
|
import { rebuildDailyFromHourly } from "./rollups.mjs";
|
|
11
11
|
import { ENGINE_QUERY_CAPABILITIES, createSqlQuerySource } from "./_chunks/index.mjs";
|
|
12
12
|
import { bindLiterals, formatLiteral } from "./sql-bind.mjs";
|
|
@@ -209,4 +209,4 @@ declare const MIN_SYNC_IMPRESSIONS = 1;
|
|
|
209
209
|
declare const MIN_COUNTRY_IMPRESSIONS = 10;
|
|
210
210
|
declare const MAX_SITEMAP_URLS_PER_SITE = 50000;
|
|
211
211
|
declare const MAX_TRACKED_URLS_PER_SITE = 200000;
|
|
212
|
-
export { type CodecCtx, type ColumnDef, type ColumnType, type CompactionThresholds, type CompactionTier, type CreateIngestAccumulatorOptions, DEFAULT_SEARCH_TYPE, type DataSource, type DateWeight, type DrizzleSchema, type DuckDBFactory, type DuckDBHandle, ENGINE_QUERY_CAPABILITIES, EngineError, EngineErrorKind, type EngineOptions, FILES_PLACEHOLDER, type FileSetRef, type FinalizeOptions, type FinalizeResult, type GcCtx, type Grain, type GscApiRow, type InMemorySink, type IngestAccumulator, type IngestAccumulatorCtx, type IngestAccumulatorEngine, type IngestAccumulatorHooks, type IngestOptions, type InspectionVerdict, type ListLiveFilter, type LockScope, MAX_DAY_BYTES, MAX_GSC_PAGES_R2, MAX_SITEMAP_URLS_PER_SITE, MAX_TRACKED_URLS_PER_SITE, MIN_COUNTRY_IMPRESSIONS, MIN_SYNC_IMPRESSIONS, type ManifestEntry, type ManifestPurgeResult, type ManifestStore, type ParquetCodec, type PurgeFilter, type PurgeResult, type PurgeUrlsResult, type QueryCtx, type QueryExecuteOptions, type QueryExecuteResult, type QueryExecutor, type QueryProfiler, type QueryResult, type QuerySpan, ROW_LIMIT_R2, type ResolvedQuery, type Row, type RowAccumulator, type RowAccumulatorOptions, type RunSQLOptions, SCHEMAS, type SchedulePolicy, type ScheduleState, type SearchType, type Sink, type SinkCapabilities, type SinkCloseResult, type SinkOptions, type SinkSlice, type SinkWriteResult, type StorageEngine, type StoredRow, type SyncState, type SyncStateDetail, type SyncStateFilter, type SyncStateKind, type SyncStateScope, type SyncTableName, TABLES_BY_SEARCH_TYPE, TABLE_METADATA, TABLE_TIERS, TIER_PRIORITY, type TableName, type TableSchema, type TableTier, type TenantCtx, type TieredTableName, WEIGHT_PRIORITY, type Watermark, type WatermarkFilter, type WatermarkScope, type WriteCtx, type WriteResult, allTables, assembleDatesRow, bindLiterals, canonicalEmptyParquetSchema, coerceRow, coerceRows, collectSpans, countries, createDuckDBCodec, createDuckDBExecutor, createIcebergResolverAdapter, createInMemorySink, createIngestAccumulator, createNoopIngestAccumulator, createParquetResolverAdapter, createQueryProfiler, createRowAccumulator, createSqlQuerySource, createStorageEngine, currentSchemaVersion, dates, dayPartition, dimensionToColumn, drizzleSchema, engineErrorToException, engineErrors, enumeratePartitions, fixedPolicy, formatEngineError, formatLiteral, getDateWeight, getTableTier, getTablesForTier, hourPartition, hourly_pages, inferLegacyTier, inferSearchType, inferTable, inspectionPolicy, isEngineError, objectKey, page_queries, pages, parseEnabledSearchTypes, pgResolverAdapter, queries, rebuildDailyFromHourly, resolveParquetSQL, sitemapPolicy, substituteNamedFiles, toPath, toSumPosition, transformGscRow, validateEnabledSearchTypes, validateEnabledSearchTypesResult };
|
|
212
|
+
export { type CodecCtx, type ColumnDef, type ColumnType, type CompactionThresholds, type CompactionTier, type CreateIngestAccumulatorOptions, DEFAULT_SEARCH_TYPE, type DataSource, type DateWeight, type DrizzleSchema, type DuckDBFactory, type DuckDBHandle, ENGINE_QUERY_CAPABILITIES, EngineError, EngineErrorKind, type EngineOptions, FILES_PLACEHOLDER, type FileSetRef, type FinalizeOptions, type FinalizeResult, type GcCtx, type Grain, type GscApiRow, type InMemorySink, type IngestAccumulator, type IngestAccumulatorCtx, type IngestAccumulatorEngine, type IngestAccumulatorHooks, type IngestOptions, type InspectionVerdict, type ListLiveFilter, type LockScope, MAX_DAY_BYTES, MAX_GSC_PAGES_R2, MAX_SITEMAP_URLS_PER_SITE, MAX_TRACKED_URLS_PER_SITE, MIN_COUNTRY_IMPRESSIONS, MIN_SYNC_IMPRESSIONS, type ManifestEntry, type ManifestPurgeResult, type ManifestStore, type ParquetCodec, type PurgeFilter, type PurgeResult, type PurgeUrlsResult, type QueryCtx, type QueryExecuteOptions, type QueryExecuteResult, type QueryExecutor, type QueryProfiler, type QueryResult, type QuerySpan, ROW_LIMIT_R2, type ResolvedQuery, type Row, type RowAccumulator, type RowAccumulatorOptions, type RunSQLOptions, SCHEMAS, type SchedulePolicy, type ScheduleState, type SearchType, type Sink, type SinkCapabilities, type SinkCloseResult, type SinkOptions, type SinkSlice, type SinkWriteResult, type StorageEngine, type StoredRow, type SyncState, type SyncStateDetail, type SyncStateFilter, type SyncStateKind, type SyncStateScope, type SyncTableName, TABLES_BY_SEARCH_TYPE, TABLE_METADATA, TABLE_TIERS, TIER_PRIORITY, type TableName, type TableSchema, type TableTier, type TenantCtx, type TieredTableName, WEIGHT_PRIORITY, type Watermark, type WatermarkFilter, type WatermarkScope, type WriteCtx, type WriteResult, allTables, assembleDatesRow, bindLiterals, canonicalEmptyParquetSchema, coerceRow, coerceRows, collectSpans, countries, createDuckDBCodec, createDuckDBExecutor, createIcebergResolverAdapter, createInMemorySink, createIngestAccumulator, createNoopIngestAccumulator, createParquetResolverAdapter, createQueryProfiler, createR2SqlResolverAdapter, createRowAccumulator, createSqlQuerySource, createStorageEngine, currentSchemaVersion, dates, dayPartition, dimensionToColumn, drizzleSchema, engineErrorToException, engineErrors, enumeratePartitions, fixedPolicy, formatEngineError, formatLiteral, getDateWeight, getTableTier, getTablesForTier, hourPartition, hourly_pages, inferLegacyTier, inferSearchType, inferTable, inspectionPolicy, isEngineError, objectKey, page_queries, pages, parseEnabledSearchTypes, pgResolverAdapter, queries, rebuildDailyFromHourly, resolveParquetSQL, sitemapPolicy, substituteNamedFiles, toPath, toSumPosition, transformGscRow, validateEnabledSearchTypes, validateEnabledSearchTypesResult };
|
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { bindLiterals, formatLiteral } from "./sql-bind.mjs";
|
|
|
8
8
|
import { MAX_DAY_BYTES, canonicalEmptyParquetSchema, createDuckDBCodec, createDuckDBExecutor, createStorageEngine } from "./_chunks/engine.mjs";
|
|
9
9
|
import { assembleDatesRow, createRowAccumulator, toPath, toSumPosition, transformGscRow } from "./ingest.mjs";
|
|
10
10
|
import "./planner.mjs";
|
|
11
|
-
import { createIcebergResolverAdapter, createParquetResolverAdapter, pgResolverAdapter } from "./_chunks/resolver.mjs";
|
|
11
|
+
import { createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, pgResolverAdapter } from "./_chunks/resolver.mjs";
|
|
12
12
|
import { rebuildDailyFromHourly } from "./rollups.mjs";
|
|
13
13
|
import { fixedPolicy, inspectionPolicy, sitemapPolicy } from "./schedule.mjs";
|
|
14
14
|
import { err, ok, unwrapResult } from "gscdump/result";
|
|
@@ -308,4 +308,4 @@ const MIN_SYNC_IMPRESSIONS = 1;
|
|
|
308
308
|
const MIN_COUNTRY_IMPRESSIONS = 10;
|
|
309
309
|
const MAX_SITEMAP_URLS_PER_SITE = 5e4;
|
|
310
310
|
const MAX_TRACKED_URLS_PER_SITE = 2e5;
|
|
311
|
-
export { DEFAULT_SEARCH_TYPE, ENGINE_QUERY_CAPABILITIES, FILES_PLACEHOLDER, MAX_DAY_BYTES, MAX_GSC_PAGES_R2, MAX_SITEMAP_URLS_PER_SITE, MAX_TRACKED_URLS_PER_SITE, MIN_COUNTRY_IMPRESSIONS, MIN_SYNC_IMPRESSIONS, ROW_LIMIT_R2, SCHEMAS, TABLES_BY_SEARCH_TYPE, TABLE_METADATA, TABLE_TIERS, TIER_PRIORITY, WEIGHT_PRIORITY, allTables, assembleDatesRow, bindLiterals, canonicalEmptyParquetSchema, coerceRow, coerceRows, collectSpans, countries, createDuckDBCodec, createDuckDBExecutor, createIcebergResolverAdapter, createInMemorySink, createIngestAccumulator, createNoopIngestAccumulator, createParquetResolverAdapter, createQueryProfiler, createRowAccumulator, createSqlQuerySource, createStorageEngine, currentSchemaVersion, dates, dayPartition, dimensionToColumn, drizzleSchema, engineErrorToException, engineErrors, enumeratePartitions, fixedPolicy, formatEngineError, formatLiteral, getDateWeight, getTableTier, getTablesForTier, hourPartition, hourly_pages, inferLegacyTier, inferSearchType, inferTable, inspectionPolicy, isEngineError, objectKey, page_queries, pages, parseEnabledSearchTypes, pgResolverAdapter, queries, rebuildDailyFromHourly, resolveParquetSQL, sitemapPolicy, substituteNamedFiles, toPath, toSumPosition, transformGscRow, validateEnabledSearchTypes, validateEnabledSearchTypesResult };
|
|
311
|
+
export { DEFAULT_SEARCH_TYPE, ENGINE_QUERY_CAPABILITIES, FILES_PLACEHOLDER, MAX_DAY_BYTES, MAX_GSC_PAGES_R2, MAX_SITEMAP_URLS_PER_SITE, MAX_TRACKED_URLS_PER_SITE, MIN_COUNTRY_IMPRESSIONS, MIN_SYNC_IMPRESSIONS, ROW_LIMIT_R2, SCHEMAS, TABLES_BY_SEARCH_TYPE, TABLE_METADATA, TABLE_TIERS, TIER_PRIORITY, WEIGHT_PRIORITY, allTables, assembleDatesRow, bindLiterals, canonicalEmptyParquetSchema, coerceRow, coerceRows, collectSpans, countries, createDuckDBCodec, createDuckDBExecutor, createIcebergResolverAdapter, createInMemorySink, createIngestAccumulator, createNoopIngestAccumulator, createParquetResolverAdapter, createQueryProfiler, createR2SqlResolverAdapter, createRowAccumulator, createSqlQuerySource, createStorageEngine, currentSchemaVersion, dates, dayPartition, dimensionToColumn, drizzleSchema, engineErrorToException, engineErrors, enumeratePartitions, fixedPolicy, formatEngineError, formatLiteral, getDateWeight, getTableTier, getTablesForTier, hourPartition, hourly_pages, inferLegacyTier, inferSearchType, inferTable, inspectionPolicy, isEngineError, objectKey, page_queries, pages, parseEnabledSearchTypes, pgResolverAdapter, queries, rebuildDailyFromHourly, resolveParquetSQL, sitemapPolicy, substituteNamedFiles, toPath, toSumPosition, transformGscRow, validateEnabledSearchTypes, validateEnabledSearchTypesResult };
|
package/dist/ingest.d.mts
CHANGED
|
@@ -16,12 +16,7 @@ interface GscApiRow {
|
|
|
16
16
|
position: number;
|
|
17
17
|
}
|
|
18
18
|
interface IngestOptions {
|
|
19
|
-
/**
|
|
20
|
-
* Canonical form of a query string, stored alongside `query` as
|
|
21
|
-
* `query_canonical`. Site-specific (e.g. synonym groups, stemming); if
|
|
22
|
-
* omitted, `query_canonical` is null. Applied to `queries` +
|
|
23
|
-
* `page_queries` tables only.
|
|
24
|
-
*/
|
|
19
|
+
/** @deprecated Canonical query data is built through query_dim, not fact rows. */
|
|
25
20
|
normalizeQuery?: (query: string) => string | null | undefined;
|
|
26
21
|
/** Date for one-day `searchAppearance` total queries, whose keys omit date. */
|
|
27
22
|
date?: string;
|
package/dist/ingest.mjs
CHANGED
|
@@ -54,7 +54,6 @@ function transformGscRow(table, apiRow, options = {}) {
|
|
|
54
54
|
date,
|
|
55
55
|
row: {
|
|
56
56
|
query,
|
|
57
|
-
query_canonical: options.normalizeQuery?.(query) ?? null,
|
|
58
57
|
date,
|
|
59
58
|
clicks,
|
|
60
59
|
impressions,
|
|
@@ -122,13 +121,11 @@ function transformGscRow(table, apiRow, options = {}) {
|
|
|
122
121
|
if (table === "search_appearance_queries") {
|
|
123
122
|
const query = String(keys[0] ?? "");
|
|
124
123
|
const date = String(keys[1] ?? "");
|
|
125
|
-
const query_canonical = options.normalizeQuery?.(query) ?? null;
|
|
126
124
|
return {
|
|
127
125
|
date,
|
|
128
126
|
row: {
|
|
129
127
|
searchAppearance: String(options.searchAppearance ?? ""),
|
|
130
128
|
query,
|
|
131
|
-
query_canonical,
|
|
132
129
|
date,
|
|
133
130
|
clicks,
|
|
134
131
|
impressions,
|
|
@@ -139,14 +136,12 @@ function transformGscRow(table, apiRow, options = {}) {
|
|
|
139
136
|
if (table === "search_appearance_page_queries") {
|
|
140
137
|
const query = String(keys[1] ?? "");
|
|
141
138
|
const date = String(keys[2] ?? "");
|
|
142
|
-
const query_canonical = options.normalizeQuery?.(query) ?? null;
|
|
143
139
|
return {
|
|
144
140
|
date,
|
|
145
141
|
row: {
|
|
146
142
|
searchAppearance: String(options.searchAppearance ?? ""),
|
|
147
143
|
url: toPath(String(keys[0] ?? "")),
|
|
148
144
|
query,
|
|
149
|
-
query_canonical,
|
|
150
145
|
date,
|
|
151
146
|
clicks,
|
|
152
147
|
impressions,
|
|
@@ -157,13 +152,11 @@ function transformGscRow(table, apiRow, options = {}) {
|
|
|
157
152
|
if (table === "dates") throw new Error("`dates` rows must be built via assembleDatesRow, not transformGscRow");
|
|
158
153
|
const query = String(keys[1] ?? "");
|
|
159
154
|
const date = String(keys[2] ?? "");
|
|
160
|
-
const query_canonical = options.normalizeQuery?.(query) ?? null;
|
|
161
155
|
return {
|
|
162
156
|
date,
|
|
163
157
|
row: {
|
|
164
158
|
url: toPath(String(keys[0] ?? "")),
|
|
165
159
|
query,
|
|
166
|
-
query_canonical,
|
|
167
160
|
date,
|
|
168
161
|
clicks,
|
|
169
162
|
impressions,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SearchType as SearchType$1, TableName as TableName$1 } from "../_chunks/storage.mjs";
|
|
2
2
|
import { ComparisonFilter, ExtraQuery, ResolvedComparisonSQL, ResolvedSQL, ResolvedSQLOptimized, ResolverAdapter, ResolverOptions } from "../_chunks/types.mjs";
|
|
3
|
-
import { PgTableKey, ResolverAdapterOptions, createIcebergResolverAdapter, createParquetResolverAdapter, pgResolverAdapter } from "../_chunks/pg-adapter.mjs";
|
|
3
|
+
import { PgTableKey, R2SqlResolverAdapterOptions, ResolverAdapterOptions, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, pgResolverAdapter } from "../_chunks/pg-adapter.mjs";
|
|
4
4
|
import { LogicalDataset, LogicalDataset as LogicalDataset$1, LogicalQueryPlan, PlannerCapabilities, UnresolvableDatasetError, inferDataset as inferLogicalDataset, isDatasetResolvable } from "gscdump/query/plan";
|
|
5
5
|
import { SQL } from "drizzle-orm";
|
|
6
6
|
import { BuilderState, Dimension, FilterInput, InternalFilter, Metric } from "gscdump/query";
|
|
@@ -36,17 +36,19 @@ interface SqlFragmentsConfig<TableKey extends string> {
|
|
|
36
36
|
*/
|
|
37
37
|
tableRef?: (tableKey: TableKey) => SQL;
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
40
|
-
* `queryCanonical` dimension expression falls back to the raw `query` when
|
|
41
|
-
* the stored `query_canonical` is NULL (no normalizer ran at ingest) or `''`
|
|
42
|
-
* (a fully-stripped query like "free online"), i.e.
|
|
43
|
-
* `COALESCE(NULLIF(query_canonical, ''), query)`. This makes canonical a
|
|
44
|
-
* TOTAL key, valid for GROUP BY / comparison joins.
|
|
39
|
+
* Where `queryCanonical` comes from when compiling fact-table reads.
|
|
45
40
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
41
|
+
* - `queryDim` (default): LEFT JOIN a query dimension relation and derive
|
|
42
|
+
* `COALESCE(query_dim.query_canonical, fact.query)`.
|
|
43
|
+
* - `column`: read a `query_canonical` column from the primary relation. This
|
|
44
|
+
* is reserved for derived canonical rollup relations, not fact tables.
|
|
49
45
|
*/
|
|
46
|
+
queryCanonicalSource?: 'queryDim' | 'column';
|
|
47
|
+
/** Relation used for the query-dimension join when `queryCanonicalSource=queryDim`. */
|
|
48
|
+
queryDimTableRef?: () => SQL;
|
|
49
|
+
/** When true, also joins query_dim on site_id (SQLite/D1 dimension table). */
|
|
50
|
+
queryDimSiteScoped?: boolean;
|
|
51
|
+
/** @deprecated Canonical reads now derive from query_dim or canonical rollups. */
|
|
50
52
|
canonicalFallback?: boolean;
|
|
51
53
|
}
|
|
52
54
|
interface SqlFragments<TableKey extends string> {
|
|
@@ -59,6 +61,9 @@ interface SqlFragments<TableKey extends string> {
|
|
|
59
61
|
urlToPathExpr: (col: string) => string;
|
|
60
62
|
colRef: (tableKey: TableKey, colName: string) => SQL;
|
|
61
63
|
tableRef: (tableKey: TableKey) => SQL;
|
|
64
|
+
fromSql: (tableKey: TableKey, options?: {
|
|
65
|
+
queryCanonical?: boolean;
|
|
66
|
+
}) => SQL;
|
|
62
67
|
dateColRef: (tableKey: TableKey) => SQL;
|
|
63
68
|
siteIdColRef?: (tableKey: TableKey) => SQL;
|
|
64
69
|
searchTypeColRef?: (tableKey: TableKey) => SQL;
|
|
@@ -81,9 +86,8 @@ declare function createResolverAdapter<TableKey extends string>(config: CreateRe
|
|
|
81
86
|
/**
|
|
82
87
|
* True when `plan` can be served from the canonical-grained rollup instead of
|
|
83
88
|
* the raw `queries` fact partitions. Conservative: anything that would read a
|
|
84
|
-
* dropped column or the raw row grain disqualifies the query
|
|
85
|
-
*
|
|
86
|
-
* data.
|
|
89
|
+
* dropped column or the raw row grain disqualifies the query. Callers decide
|
|
90
|
+
* whether that miss is a typed raw fallback or a hard coverage error.
|
|
87
91
|
*/
|
|
88
92
|
declare function planCoveredByCanonicalRollup(plan: LogicalQueryPlan): boolean;
|
|
89
93
|
/** State-level convenience: build the plan then gate. */
|
|
@@ -133,6 +137,21 @@ interface RunSQLFn {
|
|
|
133
137
|
rows: Array<Record<string, unknown>>;
|
|
134
138
|
}>;
|
|
135
139
|
}
|
|
140
|
+
interface CanonicalQueryDimSource {
|
|
141
|
+
/** Object keys for the versioned query_dim parquet. */
|
|
142
|
+
keys: string[];
|
|
143
|
+
normalizerVersion: number;
|
|
144
|
+
intentVersion?: number;
|
|
145
|
+
builtAt?: number;
|
|
146
|
+
}
|
|
147
|
+
interface PrimaryColumnarSource {
|
|
148
|
+
/** Pre-resolved compacted/Iceberg parquet object keys for the requested fact table. */
|
|
149
|
+
keys: string[];
|
|
150
|
+
/** Oldest covered date (`YYYY-MM-DD`). Omit to assert the source covers all older dates. */
|
|
151
|
+
coversFrom?: string;
|
|
152
|
+
/** Newest covered date (`YYYY-MM-DD`). Omit to assert the source covers the current tail. */
|
|
153
|
+
coversThrough?: string;
|
|
154
|
+
}
|
|
136
155
|
/**
|
|
137
156
|
* Optional overlay that serves a resolver extra (e.g. canonical-variant
|
|
138
157
|
* grouping, keyed `'canonicalExtras'`) from a precomputed source — typically a
|
|
@@ -157,36 +176,75 @@ interface RunOptimizedQueryOptions {
|
|
|
157
176
|
/** Overlay tried per extra before the live SQL; absent → today's live path. */
|
|
158
177
|
resolveExtra?: ResolveExtraFn;
|
|
159
178
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* nullable column. See ADR-0018.
|
|
179
|
+
* Deprecated compatibility flag. Canonical reads now derive from query_dim,
|
|
180
|
+
* not a nullable fact-table `query_canonical` column.
|
|
163
181
|
*/
|
|
164
182
|
canonicalFallback?: boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Versioned query dimension backing canonical reads over fact rows. Required
|
|
185
|
+
* whenever a query groups or filters by `queryCanonical`; a canonical rollup
|
|
186
|
+
* source may carry the same metadata/key set via `canonicalSource.queryDim`.
|
|
187
|
+
*/
|
|
188
|
+
queryDim?: CanonicalQueryDimSource;
|
|
189
|
+
/**
|
|
190
|
+
* Primary fact-file source for consumer reads. Callers pass compacted or
|
|
191
|
+
* Iceberg data-file keys with explicit coverage metadata; raw daily
|
|
192
|
+
* partitions are only used when `primarySourceFallback: 'raw'` is set.
|
|
193
|
+
*/
|
|
194
|
+
primarySource?: PrimaryColumnarSource;
|
|
195
|
+
/**
|
|
196
|
+
* Explicit compatibility escape hatch for raw daily shards. Default is
|
|
197
|
+
* strict: missing/empty/stale primary coverage fails with
|
|
198
|
+
* `QuerySourceCoverageError`.
|
|
199
|
+
*/
|
|
200
|
+
primarySourceFallback?: 'raw';
|
|
165
201
|
/**
|
|
166
202
|
* Opt-in canonical-primary performance (ADR-0018 Gap 2): object keys of the
|
|
167
203
|
* `query_canonical_daily` rollup parquet(s). When supplied AND the query is
|
|
168
|
-
* coverable (`canonicalRollupCovers`) AND
|
|
169
|
-
*
|
|
204
|
+
* coverable (`canonicalRollupCovers`) AND the window is within the rollup's
|
|
205
|
+
* coverage, the MAIN query reads these
|
|
170
206
|
* pre-summed `(query_canonical × date)` rows instead of re-aggregating raw
|
|
171
|
-
* partitions
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* `canonicalFallback` is REQUIRED: the rollup is built with
|
|
175
|
-
* `COALESCE(NULLIF(query_canonical, ''), query)` (fallback semantics), so
|
|
176
|
-
* serving it to a legacy (`canonicalFallback: false`) caller would change
|
|
177
|
-
* NULL/'' rows from legacy buckets to raw-query keys. The rollup is already
|
|
178
|
-
* null-free, so the rollup READ itself runs without fallback.
|
|
207
|
+
* partitions. Variant extras still need fact-grain rows, so they read
|
|
208
|
+
* `primarySource` when present and raw partitions only via
|
|
209
|
+
* `primarySourceFallback: 'raw'`.
|
|
179
210
|
*
|
|
180
211
|
* `coversThrough` (ISO `YYYY-MM-DD`, the rollup's newest covered date) gates
|
|
181
212
|
* staleness: the source is used only when `dateRange.endDate <= coversThrough`,
|
|
182
|
-
* else
|
|
183
|
-
*
|
|
213
|
+
* else strict mode fails so the recent tail is never silently undercounted.
|
|
214
|
+
* Omit to assert full coverage (use with care).
|
|
184
215
|
*/
|
|
185
216
|
canonicalSource?: {
|
|
186
217
|
keys: string[];
|
|
187
218
|
coversThrough?: string;
|
|
219
|
+
queryDim?: CanonicalQueryDimSource;
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* @deprecated Canonical-source misses are recorded on `source.fallbacks`;
|
|
223
|
+
* raw daily fallback is controlled by `primarySourceFallback: 'raw'`.
|
|
224
|
+
*/
|
|
225
|
+
canonicalSourceFallback?: 'raw';
|
|
226
|
+
/** Optional version gates for the query dimension backing `canonicalSource`. */
|
|
227
|
+
canonicalRequirements?: {
|
|
228
|
+
normalizerVersion?: number;
|
|
229
|
+
intentVersion?: number;
|
|
188
230
|
};
|
|
189
231
|
}
|
|
232
|
+
type QuerySourceKind = 'canonical-rollup' | 'primary-columnar' | 'raw-partitions';
|
|
233
|
+
type QuerySourceFallbackKind = 'primary-source-missing' | 'primary-source-empty' | 'primary-source-missing-coverage' | 'primary-source-stale-coverage' | 'canonical-source-missing' | 'canonical-source-empty' | 'canonical-source-not-coverable' | 'canonical-source-stale-coverage' | 'query-dim-missing' | 'query-dim-empty' | 'query-dim-stale-version';
|
|
234
|
+
interface QuerySourceFallback {
|
|
235
|
+
kind: QuerySourceFallbackKind;
|
|
236
|
+
message: string;
|
|
237
|
+
}
|
|
238
|
+
interface QuerySourceDecision {
|
|
239
|
+
kind: QuerySourceKind;
|
|
240
|
+
fallback?: QuerySourceFallback;
|
|
241
|
+
fallbacks?: QuerySourceFallback[];
|
|
242
|
+
}
|
|
243
|
+
declare class QuerySourceCoverageError extends Error {
|
|
244
|
+
readonly fallback: QuerySourceFallback;
|
|
245
|
+
name: string;
|
|
246
|
+
constructor(fallback: QuerySourceFallback);
|
|
247
|
+
}
|
|
190
248
|
interface OptimizedQueryResult {
|
|
191
249
|
rows: Array<Record<string, unknown>>;
|
|
192
250
|
totalCount: number;
|
|
@@ -200,11 +258,14 @@ interface OptimizedQueryResult {
|
|
|
200
258
|
key: string;
|
|
201
259
|
rows: Array<Record<string, unknown>>;
|
|
202
260
|
}>;
|
|
261
|
+
source: QuerySourceDecision;
|
|
262
|
+
extraSource?: QuerySourceDecision;
|
|
203
263
|
}
|
|
204
264
|
interface ComparisonQueryResult {
|
|
205
265
|
rows: Array<Record<string, unknown>>;
|
|
206
266
|
totalCount: number;
|
|
207
267
|
totals: Record<string, unknown>;
|
|
268
|
+
source: QuerySourceDecision;
|
|
208
269
|
}
|
|
209
270
|
declare function runOptimizedQuery(runSQL: RunSQLFn, ctx: RunQueryCtx, state: BuilderState, dateRange: {
|
|
210
271
|
startDate: string;
|
|
@@ -219,13 +280,7 @@ declare function runComparisonQuery(runSQL: RunSQLFn, ctx: RunQueryCtx, current:
|
|
|
219
280
|
startDate: string;
|
|
220
281
|
endDate: string;
|
|
221
282
|
};
|
|
222
|
-
}, filter?: ComparisonFilter, options?:
|
|
223
|
-
canonicalFallback?: boolean;
|
|
224
|
-
canonicalSource?: {
|
|
225
|
-
keys: string[];
|
|
226
|
-
coversThrough?: string;
|
|
227
|
-
};
|
|
228
|
-
}): Promise<ComparisonQueryResult>;
|
|
283
|
+
}, filter?: ComparisonFilter, options?: RunOptimizedQueryOptions): Promise<ComparisonQueryResult>;
|
|
229
284
|
/**
|
|
230
285
|
* Host-supplied reader: return the materialised rollup's rows for an
|
|
231
286
|
* `(id, tenant, slice)`, in the exact shape the live extra produces, or `null`
|
|
@@ -281,4 +336,4 @@ interface AssertSchemaInSyncOptions {
|
|
|
281
336
|
mode: 'exact' | 'superset';
|
|
282
337
|
}
|
|
283
338
|
declare function assertSchemaInSync(options: AssertSchemaInSyncOptions): void;
|
|
284
|
-
export { type AssertSchemaInSyncOptions, type ComparisonFilter, type ComparisonQueryResult, type CreateResolverAdapterConfig, DIMENSION_SURFACES, type DimensionBinding, type DimensionSurface, type ExtraQuery, LOGICAL_DATASETS, type LogicalDataset, type LogicalDatasetDefinition, type OptimizedQueryResult, type PgTableKey, type ResolveExtraFn, type ResolvedComparisonSQL, type ResolvedSQL, type ResolvedSQLOptimized, type ResolverAdapter, type ResolverAdapterOptions, type ResolverOptions, type RollupRowsReader, type RunOptimizedQueryOptions, type RunQueryCtx, type RunSQLFn, type SqlFragments, type SqlFragmentsConfig, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, planCoveredByCanonicalRollup, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, supportsDimensionOnSurface };
|
|
339
|
+
export { type AssertSchemaInSyncOptions, type CanonicalQueryDimSource, type ComparisonFilter, type ComparisonQueryResult, type CreateResolverAdapterConfig, DIMENSION_SURFACES, type DimensionBinding, type DimensionSurface, type ExtraQuery, LOGICAL_DATASETS, type LogicalDataset, type LogicalDatasetDefinition, type OptimizedQueryResult, type PgTableKey, type PrimaryColumnarSource, QuerySourceCoverageError, type QuerySourceDecision, type QuerySourceFallback, type QuerySourceFallbackKind, type QuerySourceKind, type R2SqlResolverAdapterOptions, type ResolveExtraFn, type ResolvedComparisonSQL, type ResolvedSQL, type ResolvedSQLOptimized, type ResolverAdapter, type ResolverAdapterOptions, type ResolverOptions, type RollupRowsReader, type RunOptimizedQueryOptions, type RunQueryCtx, type RunSQLFn, type SqlFragments, type SqlFragmentsConfig, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, planCoveredByCanonicalRollup, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, supportsDimensionOnSurface };
|
package/dist/resolver/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DIMENSION_SURFACES, LOGICAL_DATASETS, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, planCoveredByCanonicalRollup, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, supportsDimensionOnSurface } from "../_chunks/resolver.mjs";
|
|
2
|
-
export { DIMENSION_SURFACES, LOGICAL_DATASETS, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, planCoveredByCanonicalRollup, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, supportsDimensionOnSurface };
|
|
1
|
+
import { DIMENSION_SURFACES, LOGICAL_DATASETS, QuerySourceCoverageError, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, planCoveredByCanonicalRollup, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, supportsDimensionOnSurface } from "../_chunks/resolver.mjs";
|
|
2
|
+
export { DIMENSION_SURFACES, LOGICAL_DATASETS, QuerySourceCoverageError, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, planCoveredByCanonicalRollup, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, supportsDimensionOnSurface };
|
package/dist/rollups.d.mts
CHANGED
|
@@ -361,10 +361,10 @@ declare const queryCanonicalVariantsRollup: RollupDef;
|
|
|
361
361
|
* these per-date sums over a window is exact — identical to aggregating the raw
|
|
362
362
|
* rows.
|
|
363
363
|
*
|
|
364
|
-
* Null-free by construction: groups by
|
|
365
|
-
* query
|
|
366
|
-
*
|
|
367
|
-
*
|
|
364
|
+
* Null-free by construction: groups by the versioned query dimension when it
|
|
365
|
+
* exists, with raw query as the fallback, so the rollup never carries a NULL/''
|
|
366
|
+
* canonical bucket and the read path can treat the rollup's `query_canonical`
|
|
367
|
+
* column as already-derived.
|
|
368
368
|
*
|
|
369
369
|
* Date-grained full history (`windowDays: null`): one rollup serves every date
|
|
370
370
|
* range (reads filter by `date`) and both windows of a comparison. Opt-in (not
|
package/dist/rollups.mjs
CHANGED
|
@@ -598,7 +598,7 @@ const queryCanonicalVariantsRollup = {
|
|
|
598
598
|
}
|
|
599
599
|
],
|
|
600
600
|
parquetSortKey: ["joinKey"],
|
|
601
|
-
async build({ engine, ctx, searchType }) {
|
|
601
|
+
async build({ engine, ctx, dataSource, searchType }) {
|
|
602
602
|
const parts = await engine.listPartitions({
|
|
603
603
|
ctx,
|
|
604
604
|
table: "queries",
|
|
@@ -607,28 +607,37 @@ const queryCanonicalVariantsRollup = {
|
|
|
607
607
|
if (parts.length === 0) return [];
|
|
608
608
|
const partitions = parts.map((p) => p.partition);
|
|
609
609
|
const byCanonical = /* @__PURE__ */ new Map();
|
|
610
|
+
const dimStore = createQueryDimStore({ dataSource });
|
|
611
|
+
const useDim = await dimStore.loadMeta(ctx) !== null;
|
|
612
|
+
const canonExpr = useDim ? "COALESCE(qd.query_canonical, q.query)" : "q.query";
|
|
610
613
|
let cursor = null;
|
|
611
614
|
for (;;) {
|
|
612
|
-
const after = cursor === null ? "" : `AND query > '${cursor.replace(/'/g, "''")}'`;
|
|
615
|
+
const after = cursor === null ? "" : `AND q.query > '${cursor.replace(/'/g, "''")}'`;
|
|
616
|
+
const fileSets = { FILES: {
|
|
617
|
+
table: "queries",
|
|
618
|
+
partitions
|
|
619
|
+
} };
|
|
620
|
+
if (useDim) fileSets.QUERY_DIM = {
|
|
621
|
+
table: "queries",
|
|
622
|
+
keys: [dimStore.parquetKey(ctx)]
|
|
623
|
+
};
|
|
613
624
|
const { rows } = await engine.runSQL({
|
|
614
625
|
ctx,
|
|
615
626
|
table: "queries",
|
|
616
627
|
...searchType !== void 0 ? { searchType } : {},
|
|
617
|
-
fileSets
|
|
618
|
-
table: "queries",
|
|
619
|
-
partitions
|
|
620
|
-
} },
|
|
628
|
+
fileSets,
|
|
621
629
|
sql: `
|
|
622
630
|
SELECT
|
|
623
|
-
|
|
624
|
-
query AS query,
|
|
625
|
-
SUM(clicks) AS clicks,
|
|
626
|
-
SUM(impressions) AS impressions,
|
|
627
|
-
SUM(sum_position) AS sum_pos
|
|
628
|
-
FROM read_parquet({{FILES}}, union_by_name = true)
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
631
|
+
${canonExpr} AS joinKey,
|
|
632
|
+
q.query AS query,
|
|
633
|
+
SUM(q.clicks) AS clicks,
|
|
634
|
+
SUM(q.impressions) AS impressions,
|
|
635
|
+
SUM(q.sum_position) AS sum_pos
|
|
636
|
+
FROM read_parquet({{FILES}}, union_by_name = true) q
|
|
637
|
+
${useDim ? "LEFT JOIN read_parquet({{QUERY_DIM}}, union_by_name = true) qd ON q.query = qd.query" : ""}
|
|
638
|
+
WHERE q.query IS NOT NULL ${after}
|
|
639
|
+
GROUP BY ${canonExpr}, q.query
|
|
640
|
+
ORDER BY q.query
|
|
632
641
|
LIMIT ${ROLLUP_PAGE_ROWS_WIDE}
|
|
633
642
|
`
|
|
634
643
|
});
|
|
@@ -663,6 +672,8 @@ const queryCanonicalVariantsRollup = {
|
|
|
663
672
|
return out;
|
|
664
673
|
}
|
|
665
674
|
};
|
|
675
|
+
const CANONICAL_DAILY_ROLLUP_FINAL_ID = "query_canonical_daily";
|
|
676
|
+
const CANONICAL_DAILY_PART_STEM = "query_canonical_daily__part";
|
|
666
677
|
const queryCanonicalDailyRollup = {
|
|
667
678
|
id: "query_canonical_daily",
|
|
668
679
|
windowDays: null,
|
|
@@ -698,7 +709,7 @@ const queryCanonicalDailyRollup = {
|
|
|
698
709
|
async build({ engine, ctx, dataSource, searchType }) {
|
|
699
710
|
const dimStore = createQueryDimStore({ dataSource });
|
|
700
711
|
const useDim = await dimStore.loadMeta(ctx) !== null;
|
|
701
|
-
const canonExpr = useDim ? `COALESCE(qd.query_canonical,
|
|
712
|
+
const canonExpr = useDim ? `COALESCE(qd.query_canonical, q.query)` : `query`;
|
|
702
713
|
return (await runWindowed({
|
|
703
714
|
engine,
|
|
704
715
|
ctx,
|
|
@@ -764,7 +775,7 @@ async function rebuildCanonicalDailyResumable(opts) {
|
|
|
764
775
|
const windowsTotal = windows.length;
|
|
765
776
|
const dimStore = createQueryDimStore({ dataSource });
|
|
766
777
|
const useDim = await dimStore.loadMeta(ctx) !== null;
|
|
767
|
-
const canonExpr = useDim ? `COALESCE(qd.query_canonical,
|
|
778
|
+
const canonExpr = useDim ? `COALESCE(qd.query_canonical, q.query)` : `query`;
|
|
768
779
|
const extraFileSets = useDim ? { QUERY_DIM: {
|
|
769
780
|
table: "queries",
|
|
770
781
|
keys: [dimStore.parquetKey(ctx)]
|
|
@@ -833,8 +844,6 @@ async function rebuildCanonicalDailyResumable(opts) {
|
|
|
833
844
|
rowsWritten: batchRows.length
|
|
834
845
|
};
|
|
835
846
|
}
|
|
836
|
-
const CANONICAL_DAILY_ROLLUP_FINAL_ID = "query_canonical_daily";
|
|
837
|
-
const CANONICAL_DAILY_PART_STEM = "query_canonical_daily__part";
|
|
838
847
|
const indexingMetadataRollup = {
|
|
839
848
|
id: "indexing_metadata",
|
|
840
849
|
windowDays: null,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/engine",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.33.0",
|
|
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,8 +181,8 @@
|
|
|
181
181
|
"dependencies": {
|
|
182
182
|
"drizzle-orm": "1.0.0-rc.3",
|
|
183
183
|
"proper-lockfile": "^4.1.2",
|
|
184
|
-
"@gscdump/contracts": "0.
|
|
185
|
-
"gscdump": "0.
|
|
184
|
+
"@gscdump/contracts": "0.33.0",
|
|
185
|
+
"gscdump": "0.33.0"
|
|
186
186
|
},
|
|
187
187
|
"devDependencies": {
|
|
188
188
|
"@duckdb/duckdb-wasm": "^1.32.0",
|