Skip to content

docs(api): /analytics/queryfields[] 只有 name / type —— 删掉示例里的 label / format,并写明两层的分工 (#6369) - #6441

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-6369-analytics-fields-label-format
Aug 7, 2026
Merged

docs(api): /analytics/queryfields[] 只有 name / type —— 删掉示例里的 label / format,并写明两层的分工 (#6369)#6441
hotlong merged 1 commit into
mainfrom
claude/issue-6369-analytics-fields-label-format

Conversation

@hotlong

@hotlong hotlong commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #6369

范围严格按分诊 15:52Z 钉死的路线 (a):只改 content/docs/api/data-api.mdx,不给运行时加键。

一、前提复核(⛔ 不照抄任何卡片的引用块)

分诊提醒过「line 360 当时读作 "name": "revenue.sum",实现时重读」。重读结果#6291 / PR #6372 已合并,origin/main5d022a1)上那三行已是 revenue_sum并且行号也变了 —— 不是 359–361,而是 373–377。全程按内容定位,不按行号。

每个 fields.push 站点的自测(自己 grep,不照抄 issue 的表)

file:line 产出 服务的面
packages/services/service-analytics/src/strategies/native-sql-strategy.ts:789 返回类型即 Array< { name: string; type: string } > /analytics/query
同上 :794 fields.push({ name: dim, type: d?.type || 'string' }) /analytics/query
同上 :799 fields.push({ name: m, type: 'number' }) /analytics/query
packages/services/service-analytics/src/strategies/objectql-strategy.ts:1175 返回类型同上 /analytics/query
同上 :1179 / :1183 维度 / 度量,均 { name, type } /analytics/query
packages/services/service-analytics/src/dataset-executor.ts:893 / 932 / 1052 / 1055 { name, type: 'number' }:1052result.fields.push(f) 透传 dataset 面
packages/services/service-analytics/src/preview-evaluator.ts:252-255 { name, type } Studio 草稿预览
packages/drivers/driver-memory/src/memory-analytics.ts:470 / 475 / 485 { name, type } /analytics/query

自测相对卡片的两处实质修正

  1. 卡片的 producer 表不完整。 issue 正文只点了 service-analytics,漏掉第二个 IAnalyticsService 实现 packages/drivers/driver-memory/src/memory-analytics.ts:470 声明 Array< { name: string; type: string } >:475/:485 两处 push)。结论不变,但审计面补全了。另外 dataset-executor.ts四处不是三处,preview-evaluator.ts 是 252–255 不是 251–254。

  2. 「运行时从不发 label / format」这句话本身过宽 —— 需要按端点收窄。 实测:packages/services/service-analytics/src/analytics-service.ts:1100-1101 明确写着 f.label = m.label / f.format = m.format(ADR-0021)。但那段在 queryDataset()(:824–1183) 里,不在 query()(:774–798) 里。

POST /analytics/query 的实际链路是 packages/runtime/src/domains/analytics.ts:95-105AnalyticsService.query()strategy.execute() 直接 return,无任何 enrich;带 label/format 的是另一张面 POST /analytics/dataset/querypackages/rest/src/rest-server.ts:6975)。

⇒ 本页这一节的前提成立,但成立的理由比 issue 正文窄。因此新增的那段话我限定在本端点,没有写成「查询结果从不带 label/format」—— 那对 dataset 面是假的。

二、改前 / 改后(并排)

改前(origin/main):

"fields": [
  { "name": "industry", "type": "string", "label": "Industry" },
  { "name": "revenue_sum", "type": "number", "label": "Revenue Sum", "format": "$0,0" },
  { "name": "count", "type": "number", "label": "Count" }
],

改后:

"fields": [
  { "name": "industry", "type": "string" },
  { "name": "revenue_sum", "type": "number" },
  { "name": "count", "type": "number" }
],

外加一个 type="info" 的 Callout 区块(紧跟响应示例之后),说明 fields[] 描述的是而不是呈现,display name 与 format 住在 cube 的 metric/dimension 定义里。分诊原话是这条的实质:"deleting two keys without it leaves the next reader to re-derive why they were wrong to expect them"

三、新增句子的事实锚(file:line,逐条读过原文,非推断)

定义处:

  • packages/spec/src/data/analytics.zod.ts:56MetricSchema
  • 同上 :58label: z.string().describe('Human readable label')
  • 同上 :72format: z.string().optional()(分诊给的 :72 复核无误)
  • 同上 :81DimensionSchema.label
  • 同上 :117CubeSchema.measures: z.record(z.string(), MetricSchema)

暴露端点:

  • packages/runtime/src/domains/analytics.ts:110-117GET /analytics/metaanalyticsService.getMeta(cube)
  • packages/services/service-analytics/src/analytics-service.ts:1184-1204 — 把 measure.label / dimension.label 映射为 title
  • packages/spec/src/contracts/analytics-service.ts:89-99CubeMeta.measures: Array< { name: string; type: string; title?: string } >

线上契约旁证(本仓自己已经写对过):

  • packages/spec/src/api/analytics.zod.ts:68-79AnalyticsResultResponseSchema 声明 fields: z.array(z.object({ name, type }))
  • content/docs/references/api/analytics.mdx:84 — 生成的参考页早已印着 fields: { name: string; type: string }[]

也就是说改动前的 data-api.mdx 同时与 spec schema 和本仓自己的生成参考页矛盾。

⚠️ 一处对分诊措辞的实测修正(据实报告,未按模板顺写)

分诊原句是「display name 与 format ... 由 GET /analytics/meta 暴露」。实测 format 并不由该端点暴露getMeta 投影成 CubeMeta,measures 只保留 { name, type, title }format 在投影中被丢弃。

所以新增句子里我只对 label 说「GET /analytics/metatitle 报出」,对 format 只说它是 metric 定义的属性 —— 没有把 format 也算进「meta 暴露」。这个缺口另立了单 #6442,不在本 PR 修。

四、反向验证(先申报方向,后执行)

两个 json 代码块由脚本 JSON.parse 后比较(沿用 #6291 那位 dev 的做法),不肉眼比对

声明 A(键已清干净) —— 改后响应示例 fields[] 每个条目的键集恰为 {name, type};同一脚本跑 origin/main 应当红(3 个 label + 1 个 format)。

实测:

===== BEFORE (origin/main) =====
FAIL  A: fields[0] key set — ["label","name","type"]
FAIL  A: fields[1] key set — ["format","label","name","type"]
FAIL  A: fields[2] key set — ["label","name","type"]
FAIL  A: no `label` anywhere in fields[] — 3 entries carry label
FAIL  A: no `format` anywhere in fields[] — 1 entries carry format
===== AFTER (worktree) =====
PASS  A: fields[0] key set — ["name","type"]
PASS  A: fields[1] key set — ["name","type"]
PASS  A: fields[2] key set — ["name","type"]
PASS  A: no `label` anywhere in fields[] — 0 entries carry label
PASS  A: no `format` anywhere in fields[] — 0 entries carry format

声明 B(请求/响应仍一致) —— #6291 刚钉的「请求 measures/dimensions ↔ 响应行 key ↔ fields[].name 三者逐字一致」在改前已经绿,本次改动不得把它弄红。

⚠️ 所以 B 的预期方向是 before 绿 / after 仍绿(保持性断言),不是模板默认的 before-红/after-绿。照模板硬写成「改前红」会是伪造,故据实申报为保持性方向。

实测(改前改后各 4 项全 PASS):

PASS  B: fields[].name === request dimensions ++ measures (order + spelling)
      — fields=["industry","revenue_sum","count"] request=["industry","revenue_sum","count"]
PASS  B: rows[0] key set === fields[].name — ["industry","revenue_sum","count"]
PASS  B: rows[1] key set === fields[].name — ["industry","revenue_sum","count"]
PASS  B: no dotted measure spelling anywhere (#6291 invariant)

声明 C(新增句子有事实支撑) —— 两个锚点必须给得出 file:line 且逐条读原文核对,不得写推断。

实测:见第三节,全部为读原文所得;并附带一处对分诊措辞的实测修正(format 不由 /analytics/meta 暴露)。

五、门禁(git add 之后跑,逐条 EXIT)

门禁 EXIT 输出摘要
pnpm check:doc-authoring 0 doc authoring guard: 365 files clean
pnpm check:role-word 0 OK (44 baselined file(s), no new occurrences)
pnpm check:nul-bytes 0 OK (scanned 6070 tracked text file(s) ... no raw ASCII control bytes)
pnpm check:docs-audit-scope 0 scope is in sync with content/docs/: 178 hand-written doc(s)
pnpm --filter @objectstack/lint run check:doc-formula-expressions 0 22 record-scoped formula example(s) across 378 files / 1393 TS blocks judged clean
pnpm check:quick-reference-counts 0 顺带跑(lint.yml 中同样作用于 content/docs)
pnpm check:adr-anchors 0 OK (37 anchored file(s))

另:grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' 自扫改动文件 —— 无命中。

⚠️ 一处自我更正

本 PR 正文最初写「派单提到的 check:doc-formula-expressions 本仓不存在」—— 那是错的,我当时只 grep 了根 package.json。它是包内脚本packages/lint),由 .github/workflows/lint.yml:914-915TypeScript Type Check 作业里跑。已补跑,EXIT=0(首次跑因新 worktree 未构建依赖而 ERR_MODULE_NOT_FOUNDpnpm --filter '@objectstack/lint^...' build 之后通过 —— AGENTS.md §9 那个陈旧产物陷阱的镜像)。

六、CI(已等到收敛,非本地绿即报)

作业 结论
ESLint(含 37 个家族门禁:response-envelope / error-code-casing / engine-double-contract / 四个 docs 门…) success
TypeScript Type Check(含 step 34 Check docs formula examples are valid CEL success
Check Documentation Links#6304 今日恢复真跑) success
Check PR Size / Auto Label / Spec property liveness / Console Pin Freshness / Test Core / Dogfood Regression Gate success
Check Changeset 首跑 failure(标签竞态:opened run 的首步实时重读标签时 skip-changeset 尚未落地),确认标签在位后 rerun_failed_jobs 一次 ⇒ success

PR 标签读回:["documentation", "skip-changeset"] —— skip-changeset 由本 agent 写入(写时标签集为空,故并集即其本身),documentation 随后由 Auto Label 追加,两者共存未互相清除。

Validate Package Dependencies 未在本 PR 出现红(#6407 / PR #6427 的 lock 面与本 PR 零交集)。

七、不在本 PR 里

  • 给运行时 fields[] 增加 label / format —— 分诊已按 startup-phase focus 原则裁掉(新键在有具名消费者拉动时才发布)。若出现真实消费者,那是 domain:services 的另一张卡。
  • ⛔ 未动 packages/**(全部只读核实)。
  • ⛔ 未动 content/docs/releases/**
  • ⛔ 未动其他 docs 页。
  • 无 changeset:纯文档、不发布任何包 ⇒ 走 skip-changeset

顺带发现,已另立单 #6442(Prime Directive #10,未自我认领)

GET /analytics/meta已声明响应契约与实际响应不是同一个形状plugin-rest-api.zod.ts:1230 为该路由指定 AnalyticsMetadataResponseSchema,后者声明 data: { cubes: CubeSchema[] }(生成参考页 content/docs/references/api/analytics.mdx:49 已照此发布),而运行时返回的是 data: CubeMeta[] —— 一个裸数组、且是更窄的投影。顺带后果就是上面第三节那处:metric 的 format 在投影里被丢掉,没有任何读端点暴露它。查重已做(analytics meta cubes response schema / AnalyticsMetadataResponseSchema / CubeMeta / getMeta cube metadata format label):无同题单。

…`label` / `format` 并写明两层的分工 (#6369)

`POST /analytics/query` 的响应示例给 `data.fields[]` 三个条目都标了 `label`、
中间那个还标了 `format`,而这个端点的任何一条产出路径都只发 `{ name, type }`。
照文档写的客户端读 `fields[i].label` 渲染表头、读 `format` 格式化金额,拿到的
是 `undefined` —— 不像拼写错误那样当场 400,而是安静地渲染出空表头。

删掉这四个键,并补一段 Callout 说明两层的分工:display name 与 format 是
cube 的 metric/dimension **定义**的属性(`MetricSchema.label` / `.format`),
从 cube 元数据读,不在查询结果里。只删键不解释,下一个读者会重新推导出同样的
错误期待。

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

vercel Bot commented Aug 7, 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 7, 2026 6:45pm

Request Review

@github-actions github-actions Bot added the size/s label Aug 7, 2026
@hotlong hotlong added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 7, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 7, 2026
@hotlong
hotlong marked this pull request as ready for review August 7, 2026 18:59
@hotlong
hotlong added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit e1f66bb Aug 7, 2026
30 of 31 checks passed
@hotlong
hotlong deleted the claude/issue-6369-analytics-fields-label-format branch August 7, 2026 19:11
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 skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs: /analytics/query 的响应示例给 data.fields[] 标了 label / format —— 运行时只发 { name, type }

2 participants