Skip to content

[client] data.find 的 canonical options 探测漏掉 limit;QueryOptionsV2.expand 声明了但从不落到传输参数 #6322

Description

@hotlong

事实(对 origin/main a682670 实测)

packages/client/src/index.tsObjectStackClient.data.find(:4081)靠一组「嗅探键」判断传入的是 canonical QueryOptionsV2 还是 legacy QueryOptions:

if ('where' in options || 'fields' in options || 'orderBy' in options || 'offset' in options) {

(:4089;ScopedProjectClient.data.find :4766 是逐字同形的第二份拷贝)

嗅探键只有四个 —— where / fields / orderBy / offsetlimit 不在其中。

实测

vitest + stub fetch,读 find 真正发出的 query string(探针跑完即删,未入库):

传入 options 实际发出的 query string
{ where, orderBy, limit, offset, fields } top=20&skip=5&sort=-created_at&select=id%2Camount&contact_id=c1
{ filter, sort, top, skip, select } top=20&skip=5&sort=-created_at&select=id%2Camount&contact_id=c1
{ limit: 20 } (空)
{ top: 20 } top=20
{ offset: 5 } skip=5
{ where, limit: 20 } top=20&contact_id=c1
{ where, expand: ['contact'] } contact_id=c1

前两行是好消息:canonical 与 legacy 的五个配对键产出逐字节相同的传输参数,翻译层本身是对的。问题在另外两处。

A. { limit: N } 单键调用被静默丢弃

limit 不是嗅探键,于是走 legacy 分支的 Object.assign(:4100);而 legacy 分支此后只读 top / skip / sort / select / filter / filters / aggregations / groupBy(:4104-4150)—— limit 从头到尾没有任何一行读它。

调用方拿到的是服务端默认页大小,HTTP 200,无警告无报错。而 data.find('task', { limit: 20 }) 恰恰是 canonical 词汇下最自然的「取前 20 条」写法:不带 filter 的分页读取,一个键都不多写。

offset 单键({ offset: 5 })因为在嗅探键里,是正常的 —— 同一个接口的两个分页键行为不一致。

B. QueryOptionsV2.expand 声明了但从不映射

expand 在 :186 声明,JSDoc 说明是 "Relations to expand (JOIN / eager-load)"。但:

  • V2 分支逐键搬运 where / fields / orderBy / limit / offset / aggregations / groupBy(:4091-4097),没有 expand;
  • legacy 分支也接不住 —— QueryOptions 根本没有对应键(V2 的 JSDoc 说 expand "replaces legacy populate",而 QueryOptions 从来没有 populate)。

所以 expand 在两条分支上都落地为空,一个字符都到不了 wire。这不是服务端不认:REST 列表路由是处理 expand 的(参见 #4240sort/select/expand 的字段校验)。是 SDK 单方面的 declared ≠ delivered(Prime Directive #10 的正例形状)。

为什么现在报

#6002(给 content/docs/kernel/runtime-services/data-service.mdx 的 Parameters 补 canonical 词汇)的实现过程中实测出来的。那一单的文件面只有 content/docs/**,且按 contract-first 不该在消费侧或文档侧糊补丁,所以 producer 侧的洞单独立单。

有一点值得 PM 权衡优先级时知道:#6002 的文档页从此会正面推荐 canonical 列(where / fields / orderBy / limit / offset),照抄这页的人会开始直接写 limit —— A 的命中面随之变大,而不是维持现状。

建议

不要逐个往嗅探列表里补键(补完 limit 还有 expand,下一个新键还会漏)。按「存在任一 canonical-only 键」判定 —— 即 where / fields / orderBy / limit / offset / expand 的全集,和 QueryOptionsV2 的声明同源,新增键自动在内。

expand 另按 ADR-0049 enforce-or-remove 二选一:补上映射,或从 QueryOptionsV2 摘掉。

两处 find 拷贝(:4081 / :4761)必须一起改 —— 它们目前逐字相同,只改一处就会分叉。

未认领,交 PM 分诊定级。

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions