Skip to content

fix(service-analytics): 只当窗口用的 timeDimensions 条目不再被补默认粒度、不再静默拆网格 (#5688) - #6003

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-5688-window-timedim-no-bucket
Aug 6, 2026
Merged

fix(service-analytics): 只当窗口用的 timeDimensions 条目不再被补默认粒度、不再静默拆网格 (#5688)#6003
hotlong merged 1 commit into
mainfrom
claude/issue-5688-window-timedim-no-bucket

Conversation

@hotlong

@hotlong hotlong commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #5688

前提重验(对 origin/main 628b028,含 fb3d99b/#5808#5963/#5716)

前提成立,三个必要条件逐字复现。 以正文的 dataset 与三条数据实测,输出与立单描述逐字节一致:

fields [{"name":"owner","type":"string","label":"Owner"},{"name":"close_date","type":"time"},{"name":"opp_count",…}]
rows   [{"owner":"u1","close_date":"2026-01","opp_count":1},
        {"owner":"u1","close_date":"2026-02","opp_count":1},
        {"owner":"u2","close_date":"2026-01","opp_count":1}]

附带症状同样成立:被投影的 close_datefields 里只有 type 没有 label;同一 dataset 里把 close_date 写进 selection.dimensions 时则有 label: "Close Date" —— 富化循环只读 selection.dimensions 的盲区。

改动

按裁决「回填判据收窄」,落在 buildQueryresolvedTimeDims 一处:未写 granularity 的条目仅当该 dimension 是本次查询的网格维度、或 selection.dateGranularity 已设时才回填默认粒度(条目自带 granularity 的走不到这里,照旧分桶并投影,#4033 不变)。

resolveDimensionGranularity 未改:它回答「桶多大」,不回答「要不要分桶」。两个问题拆开写在注释里,因为它们在同一处打架正是本单的成因 —— dataset 维度的 dateGranularity 说的是「这个日期被分组时怎么渲染」,不是「请按它分组」。

compareTo 的对齐语义(同场必答项)

定义:compareTo 不隐含分桶需求;它锚定的条目按与其它条目完全相同的判据决定是否分桶,主趟与比较趟因此按构造得到同一结论 —— runComparerunMeasurePass 回到同一个 buildQuery,网格维度与 selection.dateGranularity 都相同,只有 dateRange 被平移。

实测(比较窗口内数据,且 u1 的比较值跨两个月 = 1 + 2,单月凑不出来):

改前  [{owner:u1, close_date:'2026-01', opp_count:1, opp_count__compare:0},
       {owner:u1, close_date:'2026-02', opp_count:1, opp_count__compare:2},
       {owner:u2, close_date:'2026-01', opp_count:1, opp_count__compare:1}]
改后  [{owner:u1, opp_count:2, opp_count__compare:3},
       {owner:u2, opp_count:1, opp_count__compare:1}]

KPI 单值卡(dimensions: [])同理:改前是两行月桶、卡片读第一行,改后一行 {opp_count:3, opp_count__compare:4}

PM 机制假设 1 未被证伪 —— 未发现「窗口-only 不分桶」与「compareTo 对齐」不可同时满足的形状,不报 fork。

label 富化盲区(#5691 现状 + 顺手收掉)

先读了 #5691:它是 #5537 的 PR,已 closed/merged(2026-08-06T00:49:29Z),不是待认领的单,无撞车。它把盲区作为成对控制用例钉在 dataset-dimension-field-descriptors.test.ts 里并指向本单。

修法:fields 的维度 label 富化,查找集合从 selection.dimensions 扩到「本次 selection 通过 timeDimensions 引用到的 dataset 维度」。扩的是查找,不是投影 —— 循环仍然只富化 result.fields 里已经存在的条目,所以停留为纯窗口的条目不产生列、也拿不到描述符(#5691 在该处写下的「只富化、不造列」边界原样保留,并另加注释说明为什么没有并进 selectedDims:钻取元数据与行值 label 解析回答的是另一个问题,动它们会改钻取载荷与行值,而不是表头)。

翻 pin:承重,不是删断言

dataset-dimension-field-descriptors.test.ts 的成对控制用例是明写的翻转靶,两条都翻了,并且都加重了:

  • 除列集外,新增断言网格实质 —— 一个 owner 一行、窗口内三条记录聚成一桶(改前是 2 + 1 两行),以及「没有哪一行带着描述符没提的列」;
  • 两个 block 的用例仍然逐列相同,收敛关系不变。

新增 dataset-window-timedimension-bucketing.test.ts(11 例)钉住收窄判据的三条正向臂反向臂、compareTo 成对双向行为、以及一条「dataset 未声明 dateGranularity」的对照(证明声明本身就是触发条件,而不是假定)。

反向验证(先预测方向,再跑;拆成两半各自隔离)

方向普通(红),无反转、无计数移动。

两半各自承重,互不代偿。

范围与门禁

必答项:对 #5717(isMissingSourceError 嗅探器)的影响

完全无影响,三条独立理由:

  1. 位置不同 —— 嗅探器包在 DatasetExecutor.execute()catch 上(analytics-service.ts :796),判的是驱动抛出的 relation-missing 错误;本改动只动 buildQuery 里成功路径的 granularity 判据,不新增、不消除、不改写任何异常。
  2. 到达条件不变 —— 收窄只可能让某条 timeDimensions 条目不再进入 GROUP BY,而 isMissingSourceError 关心的是表/关系是否存在,与是否分桶正交。
  3. 已实测 —— dataset-cross-datasource-registration.test.ts 等相关用例全绿,未出现新的空结果吞没路径。

既没让它变简单,也没让它变难,更没让它变得不必要。

验证

.github/workflows/lint.yml 逐个枚举全跑(lint / slot-lookup / query-options-erasure / nul-bytes / doc-authoring / docs-audit-scope / role-word / adr-anchors / org-identifier / authz-resolver / service-providers / route-envelope / error-code-casing / wildcard-fallthrough / init-service-contract / durability-log-level / startup-registry-verdict / objectui-changeset / release-notes / release-body / node-version / workflow-status-functions / published-files / engine-double-contract / resume-authority-declared),全绿;typecheck 侧 check:type-check-coverage(本包 3 error 的实测台账未移动)/ check:driver-conformance / check:stall-guard 全绿。

@objectstack/service-analytics:59 文件 / 1092 例(改前基线)→ 60 文件 / 1103 例全绿

changeset 已加(用户可见行为变更:窗口-only selection 的行数与列集同时变化 —— 此前多出的月列与裂行消失)。


Generated by Claude Code

…buckets the grid (#5688)

A `timeDimensions` entry carrying only a `dateRange` — a dashboard date-range
filter, with the dimension absent from `selection.dimensions` — had the dataset
dimension's declared `dateGranularity` filled in, which made it a GROUP BY item:
the response grew a time column nobody selected and rows split per bucket, so
"count by Owner" plus a date filter came back as "by Owner x month".

Narrow the backfill: an entry that states no `granularity` gets one only when
the request says that date is being bucketed — the dimension is a grid dimension
of this query, or `selection.dateGranularity` is set. An entry carrying its own
granularity is unaffected and stays projected (#4033).

compareTo alignment (#3588/#4870) holds by construction: the comparison pass
re-enters `buildQuery` with the same grid dimensions, so both passes bucket an
entry alike or not at all. For a window-only anchor this repairs the comparison
— the merge keys on `dimensions` alone, so the backfilled bucket column sat
outside the key and the comparison value landed on whichever split row the index
held last. Pinned as a pair, both verdicts, both passes.

Also closes the same-root-cause label blind spot: a time column projected via
`timeDimensions` now carries its dataset `label` in `fields`. The enrichment
lookup widens; the projection does not, so a window-only entry still mints no
descriptor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 6, 2026 2:20pm

Request Review

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/service-analytics.

8 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/api/data-api.mdx (via @objectstack/service-analytics)
  • content/docs/api/index.mdx (via @objectstack/service-analytics)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/service-analytics)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/service-analytics)
  • content/docs/plugins/packages.mdx (via @objectstack/service-analytics)
  • content/docs/releases/implementation-status.mdx (via @objectstack/service-analytics)
  • content/docs/releases/v17.mdx (via @objectstack/service-analytics)
  • content/docs/releases/v9.mdx (via @objectstack/service-analytics)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@hotlong
hotlong marked this pull request as ready for review August 6, 2026 14:38
@hotlong
hotlong added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit f6385c7 Aug 6, 2026
24 checks passed
@hotlong
hotlong deleted the claude/issue-5688-window-timedim-no-bucket branch August 6, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants