Skip to content

docs(api): /analytics/query 示例的 measure 拼写改为 revenue_sum,并写明合法拼写 (#6291) - #6372

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-6291-analytics-measure-spelling
Aug 7, 2026
Merged

docs(api): /analytics/query 示例的 measure 拼写改为 revenue_sum,并写明合法拼写 (#6291)#6372
hotlong merged 1 commit into
mainfrom
claude/issue-6291-analytics-measure-spelling

Conversation

@hotlong

@hotlong hotlong commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #6291

纯文档修正。示例里 revenue.sum 的首段 revenue 不是 cube 名(cube 是 account),所以它不是 cube 名限定符,而是一个点号 measure —— 而点号 measure 今天被运行时明确拒收。

⚠️ 下文凡出现 < field >_sum / < cube >. 这类写法,尖括号后的空格是为了避开 GitHub 正文 sanitizer(它会把 < + 字母当 HTML 标签吃掉);仓库文件里是正常的无空格写法。

一、前提复核(在 worktree 对 origin/main d8e8d9c 实测)

分诊(14:05Z)把范围从正文的三处扩到六处。实测与分诊完全一致,六处:

$ git grep -n 'revenue\.sum' -- content/
content/docs/api/client-sdk.mdx:267:  measures: ['revenue.sum', 'count'],
content/docs/api/client-sdk.mdx:279:  measures: ['revenue.sum'],
content/docs/api/data-api.mdx:338:  "measures": ["revenue.sum", "count"],
content/docs/api/data-api.mdx:355:      { "industry": "Technology", "revenue.sum": 150000, "count": 5 },
content/docs/api/data-api.mdx:356:      { "industry": "Healthcare", "revenue.sum": 80000, "count": 3 }
content/docs/api/data-api.mdx:360:      { "name": "revenue.sum", "type": "number", "label": "Revenue Sum", "format": "$0,0" },
$ git grep -n 'revenue\.sum' -- content/ | wc -l
6

全仓(不限 content/)同样只有这 6 处,revenue_sum 改前 0 处。

一处措辞订正(不影响处数与站点):分诊表把 :360 记为「columns 条目」,该数组在页面上的实际 JSON 键是 fields,不是 columns。站点、行号都对,仅名称记错。

失败模式已随 #5918 变化,前提仍然成立:issue 正文描述的是 #5918 之前的行为(静默剥前缀 → 对列 sum 聚合 → 400 点名页面上没有的 sum)。#5918 的运行时半边已作为 PR #6292(commit 2bc1876)落到 main。今天的实际失败是点名原拼写的 400 且不执行任何查询(下方 C 段实测)。两种情况下「照文档抄跑不通」都成立,故前提有效;只是文档从「跑不通且报错莫名」变成了「平台明确拒收的写法仍写在文档里」。

二、请求 / 响应三段并排(声明 B)

改前请求已是 revenue.sum,响应 key 也是 revenue.sum —— 两边一致地错。只改请求不改响应会把页面变成「文档化的请求与文档化的响应 key 对不上」,比今天更糟,所以六处同批。

位置 改前 改后
请求 measures[0](data-api.mdx:338) "revenue.sum" "revenue_sum"
响应 rows[0] 的 key(:355:369) "revenue.sum": 150000 "revenue_sum": 150000
响应 rows[1] 的 key(:356:370) "revenue.sum": 80000 "revenue_sum": 80000
响应 fields[1].name(:360:374) "revenue.sum" "revenue_sum"

机器复核(把两个 json 代码块真的 JSON.parse 后比对,不是肉眼):

request  .measures      = ["revenue_sum","count"]
response .rows[].keys   = [["industry","revenue_sum","count"],["industry","revenue_sum","count"]]
response .fields[].name = ["industry","revenue_sum","count"]

rows keys     == dimensions + measures : true
fields[].name == dimensions + measures : true
B VERDICT: CONSISTENT

三、后缀约定的运行时证据(file:line,均为只读核实,本 PR 不动 packages/**)

分诊给了这句话的措辞,但按要求没有照抄当事实,逐条回到代码核实:

论断 证据
点号仅在作 cube 名限定符时合法,其余点号一律拒收 packages/services/service-analytics/src/analytics-service.ts:1905-1922 —— mintableMeasureKey():if (member.slice(0, dot) === cubeName) return member.slice(dot + 1); 否则 throw invalidMemberError(...)
拒收是 400 + INVALID_FIELD + 点名调用方原拼写 packages/services/service-analytics/src/dataset-refusal.ts:179-196 —— err.code = INVALID_FIELD; err.status = 400; err.member = meta.member;
聚合后缀表 packages/services/service-analytics/src/analytics-service.ts:1946-1953 —— inferMeasure()suffixes:_count_distinct / _sum / _avg / _average / _min / _max
countCOUNT(*)(示例里另一个 measure) analytics-service.ts:1942-1944(inferMeasure('count'))+ :1730(即席路径固定注入 measures.count)
Cube 声明的 measure 直接命中,不进铸造、不受点号规则约束 analytics-service.ts:243-263(declaredMemberEntry)、:1272-1276:1743-1748 两处铸造循环都先 verbatim 查声明
对象字段推断出的 cube 会铸 < field >_sum / < field >_avg packages/services/service-analytics/src/cube-registry.ts:104-115
运行时自己的两处诊断也教同一套后缀 analytics-service.ts:1390-1393:1916 均写 '< field >_sum' / '_avg' / '_min' / '_max' / '_count_distinct'

两处与分诊措辞不符,以实测为准:

  1. 分诊的句子漏了裸 count 照它的措辞写死(「对象自己的字段 + 聚合后缀,或 Cube 声明的 measure 名」),同一个示例里紧挨着的 "count" 会显得非法 —— 而 count 恰恰合法。故本 PR 的句子把 count 明确写进合法拼写,否则这句话会和它自己脚下的示例打架。
  2. 代码里还有 _average 作为 _avg 的别名(analytics-service.ts:1950)。本 PR 有意不在文档里推广它:运行时自己的两处报错消息教的都是那五个规范后缀,文档跟着教规范拼写;宣传别名等于把宽松消费面写进契约。此处如实报告,不改代码。

另记(不写进文档、不在本 PR 处理):inferMeasure 对无法识别的 key 兜底成 sum(key)(:1963),但当字段探针可用时会被 assertMeasureFields 拦下。这是代码里明确注释过的 best-effort 设计,非缺陷,故未立单。

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

声明 A —— 全覆盖

申报:改后 git grep -n 'revenue\.sum' -- content/ 零命中;revenue_sum 命中数 == 改动处数(6)。

实测:申报的字面形式被证伪 —— 而且是有意的,不是遗漏。 新增的防复发句子故意把 revenue.sum 作为反例引用,所以 content/ 里必然还剩 1 处命中。把申报按其真实意图精化为「任何可照抄的代码块内零命中」后重测:

revenue.sum in COPYABLE code fences : 0   ← 真正要保证的
revenue.sum in prose (named as bad) : 1   ← 新句子里的反例,位于 Callout 散文中
revenue_sum in COPYABLE code fences : 6   ← 恰为改正的六处
revenue_sum in prose                : 2   ← 新句子里的正例

按 fence 内外分类统计,不是整文件 grep。结论:六处示例站点全部改正,页面上仅存的 revenue.sum 是被明确标注为「会被拒收」的反例。

声明 B —— 请求/响应一致

申报:请求 measures 元素、响应行 key、fields[].name 三者逐字一致。实测:CONSISTENT(见第二节的 JSON.parse 比对)。

声明 C —— 运行时可跑

申报:能起栈就实跑改后的查询取证,起不来则如实报「未实跑」。

实测 —— 这一条是真实跑的,不是读代码推断。 在 worktree 内 pnpm --filter "@objectstack/service-analytics..." build(EXIT=0)后,直接驱动 built dist 的 AnalyticsService,喂文档里那条一模一样的 query(cube: accountdimensions: ['industry']where: {status:'active'}limit: 100),两种策略各跑一遍:

OLD ["revenue.sum","count"]  [NativeSQL]  → THREW
  code=INVALID_FIELD status=400 param=measures member="revenue.sum" field=undefined
  nothing executed? sqls=[] calls=[]

OLD ["revenue.sum","count"]  [ObjectQL]   → THREW   (同上,逐字段一致)

NEW ["revenue_sum","count"]  [NativeSQL]  → OK
  fields[].name = ["industry","revenue_sum","count"]
  SQL = SELECT industry AS "industry", SUM(revenue) AS "revenue_sum", COUNT(*) AS "count"
        FROM "account" WHERE status = $1 GROUP BY industry LIMIT 100

NEW ["revenue_sum","count"]  [ObjectQL]   → OK
  aggregations = [{"field":"revenue","method":"sum","alias":"revenue_sum"},
                  {"field":"*","method":"count","alias":"count"}]

QUALIFIER ["account.revenue_sum","count"] [NativeSQL] → OK
  SQL = ... SUM(revenue) AS "account.revenue_sum" ...

三点判读:

  • 改前的示例今天确实跑不通,且两种策略下都是 400 点名 revenue.sum 本身、一条语句都没执行
  • 改后的示例确实表达「对 revenue 求和」 —— SUM(revenue),别名 revenue_sum,与文档响应里的 key 逐字相同。
  • 限定符那句话按实测收紧过措辞:account.revenue_sum 合法且解析到同一 measure(SUM(revenue)),但输出列名保留调用方发的那个拼写(AS "account.revenue_sum")。初稿写的是「限定符会被剥掉」,实测发现只在解析度量时剥、响应列名不剥,已据实改成「响应列名保留你发的那个拼写」。

取证口径:驱动用的是 stub(照搬 __tests__/dotted-measure-refusal.test.tsmakeService() 形状),所以 rows 是我喂给它的、不构成对行 key 的证据;真正由服务产出、因而可作证据的是生成的 SQL 别名fields[] 描述符 —— 而真实驱动的行正是按 SQL 别名成 key 的。

五、门禁(均在 git add 之后运行)

EXIT 备注
pnpm check:doc-authoring 0 365 files clean — no bare metadata literals
pnpm check:role-word 0 OK (44 baselined file(s), no new occurrences)
pnpm check:nul-bytes 0 scanned 6010 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 check:doc-formula-expressions 0 22 record-scoped formula example(s) across 378 files / 1397 TS blocks judged clean

check:doc-formula-expressions 首跑红过一次,原因是 @objectstack/formula 未构建(ERR_MODULE_NOT_FOUND … formula/dist/index.mjs)—— 新 worktree 的依赖未构建,与本改动无关;pnpm --filter "@objectstack/formula..." build 后复跑 EXIT=0。没有绕过任何门。

补充自检:对两个改动文件跑 grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]',无命中。

链接面:lychee 在本容器未安装,未能本地预扫;但本 PR 未新增/未修改任何链接(diff 里没有 ]( 或裸 URL 的增减),故对 Check Documentation Links 的风险为零 —— 以 PR 上该 job 的实际结论为准。

六、不在本 PR 里


Generated by Claude Code

示例里 `revenue.sum` 的首段 `revenue` 不是 cube 名(cube 是 `account`),
所以它不是 cube 名限定符。#5918(PR #6292)落地后,这条示例在运行时得到
400 INVALID_FIELD 并点名 `revenue.sum` 本身,且不执行任何查询 —— 即照
文档抄的查询跑不通。

六处同批改正(请求 + 响应两个半边,避免出现「文档化的请求与文档化的
响应 key 对不上」),并在 data-api.mdx 的 /analytics/query 段落写明合法
拼写:对象自己的字段 + 聚合后缀,或 Cube 声明的 measure 名;点号仅在
作 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 3:22pm

Request Review

@hotlong hotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 7, 2026 — with Claude
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Aug 7, 2026
@hotlong
hotlong marked this pull request as ready for review August 7, 2026 15:38
@hotlong
hotlong added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 22a2b60 Aug 7, 2026
24 checks passed
@hotlong
hotlong deleted the claude/issue-6291-analytics-measure-spelling branch August 7, 2026 16:04
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/s 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 的两处示例用 measures: ['revenue.sum'] —— 首段不是 cube 名,这条查询今天就答 400

2 participants