@gscdump/engine 0.31.5 → 0.31.7

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.
@@ -220,6 +220,8 @@ declare const WINDOW_BYTE_BUDGET: number;
220
220
  */
221
221
  declare const ROLLUP_PAGE_ROWS = 50000;
222
222
  declare const ROLLUP_PAGE_ROWS_WIDE = 20000;
223
+ declare const ROLLUP_PAGE_ROWS_DAILY = 90000;
224
+ declare const DAILY_MAX_WINDOW_DAYS = 7;
223
225
  /**
224
226
  * UTC day-aligned [startMs, endMs] span a partition covers. Returns null for
225
227
  * `hourly/` partitions and anything unrecognised — those are excluded from
@@ -240,7 +242,7 @@ declare function planRollupWindows(parts: Array<{
240
242
  }>, clampRange?: {
241
243
  start: string;
242
244
  end: string;
243
- }): Array<{
245
+ }, maxWindowDays?: number): Array<{
244
246
  start: string;
245
247
  end: string;
246
248
  partitions: string[];
@@ -280,7 +282,8 @@ declare function runWindowed(opts: {
280
282
  paginate?: {
281
283
  orderBy: string;
282
284
  pageRows: number;
283
- };
285
+ }; /** Cap each window's day span (output-cardinality bound). See `planRollupWindows`. */
286
+ maxWindowDays?: number;
284
287
  }): Promise<Row$1[]>;
285
288
  /**
286
289
  * Daily totals across the full history. One row per (date, table) with
@@ -446,4 +449,4 @@ declare const DEFAULT_ROLLUPS: readonly RollupDef[];
446
449
  * (CLI: `gscdump rollups --with-canonical`).
447
450
  */
448
451
  declare const CANONICAL_ROLLUPS: readonly RollupDef[];
449
- export { CANONICAL_ROLLUPS, DEFAULT_ROLLUPS, ParquetRollupPointer, ROLLUP_PAGE_ROWS, ROLLUP_PAGE_ROWS_WIDE, RebuildDailyFromHourlyOptions, RebuildRollupResult, RebuildRollupsOptions, RollupBucket, RollupCtx, RollupDef, RollupEngine, RollupEnvelope, WINDOW_BYTE_BUDGET, dailyTotalsRollup, indexPercentRollup, indexingHealthRollup, indexingMetadataRollup, partitionDaySpan, partitionsInRange, planRollupWindows, queryCanonicalDailyRollup, queryCanonicalVariantsRollup, readLatestRollup, rebuildDailyFromHourly, rebuildRollups, rollupKey, rollupParquetKey, runWindowed, sitemapChanges28dRollup, sitemapHealthRollup, topCountries28dRollup, topKeywords28dParquetRollup, topKeywords28dRollup, topPages28dRollup, weeklyTotalsRollup };
452
+ export { CANONICAL_ROLLUPS, DAILY_MAX_WINDOW_DAYS, DEFAULT_ROLLUPS, ParquetRollupPointer, ROLLUP_PAGE_ROWS, ROLLUP_PAGE_ROWS_DAILY, ROLLUP_PAGE_ROWS_WIDE, RebuildDailyFromHourlyOptions, RebuildRollupResult, RebuildRollupsOptions, RollupBucket, RollupCtx, RollupDef, RollupEngine, RollupEnvelope, WINDOW_BYTE_BUDGET, dailyTotalsRollup, indexPercentRollup, indexingHealthRollup, indexingMetadataRollup, partitionDaySpan, partitionsInRange, planRollupWindows, queryCanonicalDailyRollup, queryCanonicalVariantsRollup, readLatestRollup, rebuildDailyFromHourly, rebuildRollups, rollupKey, rollupParquetKey, runWindowed, sitemapChanges28dRollup, sitemapHealthRollup, topCountries28dRollup, topKeywords28dParquetRollup, topKeywords28dRollup, topPages28dRollup, weeklyTotalsRollup };
package/dist/rollups.mjs CHANGED
@@ -129,6 +129,8 @@ function utcDateMinusDays(at, days) {
129
129
  const WINDOW_BYTE_BUDGET = 10 * 1024 * 1024;
130
130
  const ROLLUP_PAGE_ROWS = 5e4;
131
131
  const ROLLUP_PAGE_ROWS_WIDE = 2e4;
132
+ const ROLLUP_PAGE_ROWS_DAILY = 9e4;
133
+ const DAILY_MAX_WINDOW_DAYS = 7;
132
134
  const DAY_RE = /^daily\/(\d{4})-(\d{2})-(\d{2})$/;
133
135
  const WEEK_RE = /^weekly\/(\d{4})-(\d{2})-(\d{2})$/;
134
136
  const MONTH_RE = /^monthly\/(\d{4})-(\d{2})$/;
@@ -177,7 +179,7 @@ function partitionDaySpan(partition) {
177
179
  function clamp(n, lo, hi) {
178
180
  return Math.max(lo, Math.min(hi, n));
179
181
  }
180
- function planRollupWindows(parts, clampRange) {
182
+ function planRollupWindows(parts, clampRange, maxWindowDays) {
181
183
  const clampStartMs = clampRange ? Date.parse(`${clampRange.start}T00:00:00Z`) : void 0;
182
184
  const clampEndMs = clampRange ? Date.parse(`${clampRange.end}T00:00:00Z`) : void 0;
183
185
  const spans = [];
@@ -202,7 +204,8 @@ function planRollupWindows(parts, clampRange) {
202
204
  const totalBytes = spans.reduce((a, s) => a + s.bytes, 0);
203
205
  const spanDays = Math.floor((rangeEndMs - rangeStartMs) / MS_PER_DAY) + 1;
204
206
  const bytesPerDay = Math.max(1, totalBytes / spanDays);
205
- const windowDays = clamp(Math.floor(WINDOW_BYTE_BUDGET / bytesPerDay), 7, 400);
207
+ const byteWindowDays = clamp(Math.floor(WINDOW_BYTE_BUDGET / bytesPerDay), 7, 400);
208
+ const windowDays = maxWindowDays != null ? Math.max(1, Math.min(byteWindowDays, maxWindowDays)) : byteWindowDays;
206
209
  const windows = [];
207
210
  let cursorMs = rangeStartMs;
208
211
  while (cursorMs <= rangeEndMs) {
@@ -248,7 +251,7 @@ async function runWindowed(opts) {
248
251
  ctx: opts.ctx,
249
252
  table: opts.table,
250
253
  ...opts.searchType !== void 0 ? { searchType: opts.searchType } : {}
251
- }));
254
+ }), void 0, opts.maxWindowDays);
252
255
  const rows = [];
253
256
  for (const w of windows) {
254
257
  const fileSets = {
@@ -603,44 +606,61 @@ const queryCanonicalVariantsRollup = {
603
606
  });
604
607
  if (parts.length === 0) return [];
605
608
  const partitions = parts.map((p) => p.partition);
606
- return (await runPagedQuery({
607
- engine,
608
- ctx,
609
- table: "queries",
610
- ...searchType !== void 0 ? { searchType } : {},
611
- fileSets: { FILES: {
609
+ const byCanonical = /* @__PURE__ */ new Map();
610
+ let cursor = null;
611
+ for (;;) {
612
+ const after = cursor === null ? "" : `AND query > '${cursor.replace(/'/g, "''")}'`;
613
+ const { rows } = await engine.runSQL({
614
+ ctx,
612
615
  table: "queries",
613
- partitions
614
- } },
615
- orderBy: "joinKey",
616
- pageRows: ROLLUP_PAGE_ROWS_WIDE,
617
- coreSql: `
618
- WITH per_variant AS (
616
+ ...searchType !== void 0 ? { searchType } : {},
617
+ fileSets: { FILES: {
618
+ table: "queries",
619
+ partitions
620
+ } },
621
+ sql: `
619
622
  SELECT
620
623
  COALESCE(NULLIF(query_canonical, ''), query) AS joinKey,
621
624
  query AS query,
622
625
  SUM(clicks) AS clicks,
623
626
  SUM(impressions) AS impressions,
624
- SUM(sum_position) AS sum_pos,
625
- ROW_NUMBER() OVER (PARTITION BY COALESCE(NULLIF(query_canonical, ''), query) ORDER BY SUM(clicks) DESC) AS rn,
626
- COUNT(*) OVER (PARTITION BY COALESCE(NULLIF(query_canonical, ''), query)) AS variantCount
627
+ SUM(sum_position) AS sum_pos
627
628
  FROM read_parquet({{FILES}}, union_by_name = true)
629
+ WHERE query IS NOT NULL ${after}
628
630
  GROUP BY COALESCE(NULLIF(query_canonical, ''), query), query
629
- )
630
- SELECT
631
- joinKey,
632
- MAX(variantCount)::BIGINT AS variantCount,
633
- MAX(CASE WHEN rn = 1 THEN query END) AS canonicalName,
634
- GROUP_CONCAT(CASE WHEN rn <= 10 THEN query || ':::' || clicks || ':::' || impressions || ':::' || CAST(ROUND(CAST(sum_pos AS REAL) / NULLIF(impressions, 0) + 1, 1) AS TEXT) END, '||') AS variants
635
- FROM per_variant
636
- GROUP BY joinKey
637
- `
638
- })).map((r) => ({
639
- joinKey: String(r.joinKey),
640
- variantCount: BigInt(r.variantCount),
641
- canonicalName: r.canonicalName == null ? null : String(r.canonicalName),
642
- variants: r.variants == null ? null : String(r.variants)
643
- }));
631
+ ORDER BY query
632
+ LIMIT ${ROLLUP_PAGE_ROWS_WIDE}
633
+ `
634
+ });
635
+ for (const r of rows) {
636
+ const joinKey = String(r.joinKey);
637
+ const list = byCanonical.get(joinKey);
638
+ const v = {
639
+ query: String(r.query),
640
+ clicks: Number(r.clicks),
641
+ impressions: Number(r.impressions),
642
+ sumPos: Number(r.sum_pos)
643
+ };
644
+ if (list) list.push(v);
645
+ else byCanonical.set(joinKey, [v]);
646
+ }
647
+ if (rows.length < 2e4) break;
648
+ cursor = String(rows[rows.length - 1].query);
649
+ }
650
+ const out = [];
651
+ for (const [joinKey, variants] of byCanonical) {
652
+ variants.sort((a, b) => b.clicks - a.clicks || a.query.localeCompare(b.query));
653
+ const canonicalName = variants[0]?.query ?? null;
654
+ const top = variants.slice(0, 10).filter((v) => v.impressions > 0);
655
+ const variantsStr = top.length === 0 ? null : top.map((v) => `${v.query}:::${v.clicks}:::${v.impressions}:::${(v.sumPos / v.impressions + 1).toFixed(1)}`).join("||");
656
+ out.push({
657
+ joinKey,
658
+ variantCount: BigInt(variants.length),
659
+ canonicalName,
660
+ variants: variantsStr
661
+ });
662
+ }
663
+ return out;
644
664
  }
645
665
  };
646
666
  const queryCanonicalDailyRollup = {
@@ -690,8 +710,9 @@ const queryCanonicalDailyRollup = {
690
710
  } } } : {},
691
711
  paginate: {
692
712
  orderBy: "date, query_canonical",
693
- pageRows: ROLLUP_PAGE_ROWS
713
+ pageRows: ROLLUP_PAGE_ROWS_DAILY
694
714
  },
715
+ maxWindowDays: 7,
695
716
  sqlFor: useDim ? (w) => `
696
717
  SELECT
697
718
  ${canonExpr} AS query_canonical,
@@ -1035,4 +1056,4 @@ const DEFAULT_ROLLUPS = [
1035
1056
  sitemapChanges28dRollup
1036
1057
  ];
1037
1058
  const CANONICAL_ROLLUPS = [queryCanonicalVariantsRollup, queryCanonicalDailyRollup];
1038
- export { CANONICAL_ROLLUPS, DEFAULT_ROLLUPS, ROLLUP_PAGE_ROWS, ROLLUP_PAGE_ROWS_WIDE, WINDOW_BYTE_BUDGET, dailyTotalsRollup, indexPercentRollup, indexingHealthRollup, indexingMetadataRollup, partitionDaySpan, partitionsInRange, planRollupWindows, queryCanonicalDailyRollup, queryCanonicalVariantsRollup, readLatestRollup, rebuildDailyFromHourly, rebuildRollups, rollupKey, rollupParquetKey, runWindowed, sitemapChanges28dRollup, sitemapHealthRollup, topCountries28dRollup, topKeywords28dParquetRollup, topKeywords28dRollup, topPages28dRollup, weeklyTotalsRollup };
1059
+ export { CANONICAL_ROLLUPS, DAILY_MAX_WINDOW_DAYS, DEFAULT_ROLLUPS, ROLLUP_PAGE_ROWS, ROLLUP_PAGE_ROWS_DAILY, ROLLUP_PAGE_ROWS_WIDE, WINDOW_BYTE_BUDGET, dailyTotalsRollup, indexPercentRollup, indexingHealthRollup, indexingMetadataRollup, partitionDaySpan, partitionsInRange, planRollupWindows, queryCanonicalDailyRollup, queryCanonicalVariantsRollup, readLatestRollup, rebuildDailyFromHourly, rebuildRollups, rollupKey, rollupParquetKey, runWindowed, sitemapChanges28dRollup, sitemapHealthRollup, topCountries28dRollup, topKeywords28dParquetRollup, topKeywords28dRollup, topPages28dRollup, weeklyTotalsRollup };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/engine",
3
3
  "type": "module",
4
- "version": "0.31.5",
4
+ "version": "0.31.7",
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": "0.31.5",
185
- "@gscdump/contracts": "0.31.5"
184
+ "@gscdump/contracts": "0.31.7",
185
+ "gscdump": "0.31.7"
186
186
  },
187
187
  "devDependencies": {
188
188
  "@duckdb/duckdb-wasm": "^1.32.0",