发现于 #3419(把 ObjectDataPage「Save as view」的 URL 下钻三元组折成 spec rule)。不在该 PR 的 fence 内,也没有在真后端上验证过,故按 observation 记录,不带 pm:queue。
观察
已保存视图的 filter 落盘形状是 spec 的 ViewFilterRule[]({ field, operator, value })—— objectstack#5159 的修法就是让 persistViewFilter 写 folded.rules。读回来时:
ObjectView.tsx(约 1347 行)const base = viewDef.filter ?? listSchema.filter,再 [...baseArr, ...urlFilters] —— 这里会把 rule 对象和 URL 三元组混在同一个数组里;
- 传给
ListView 的 schema.filter,ListView.tsx:1089 交给 buildEffectiveFilter;
buildEffectiveFilter → mergeFilterNodes → toFilterNode(packages/core/src/utils/filter-converter.ts:199),而 toFilterNode 对数组是原样返回:
if (Array.isArray(source)) return source.length > 0 ? (source as FilterNode) : undefined;
也就是说 [{ field: 'stage', operator: 'equals', value: 'open' }] 会被当成一个 FilterNode 直接进 $filter。客户端这一路没有任何地方把 rule 对象折成 [field, op, value](ListView.mapOperator 只在 convertFilterGroupToAST 里被调用,处理的是 FilterBuilder 的 group,不覆盖 schema.filter)。
值得注意的是 mergeFilterNodes 自己的注释就警告过这个形状:
spreading a ViewFilterRule[] puts bare rule OBJECTS where the AST expects nodes, and the server neither understands nor rejects that cleanly —— isFilterAST says no (a 400 since objectstack#4121), while parseFilterAST reads the rule as a Mongo condition and filters on columns literally named field / operator / value.
注释是针对 spread 写的,但 toFilterNode 的原样返回把同一个数组整体交出去,落到 $filter 上是同一类载荷。
为什么标成 observation 而不是缺陷
两种可能我没能区分,需要一个跑起来的后端才能定:
- 服务端本来就接受
ViewFilterRule[] —— 那这条只是注释与实现的语气不一致,顺手补一句说明即可;
- 服务端不接受 —— 那么今天每一条带 filter 的已保存视图筛选都是坏的(400 或静默按字面列名过滤)。若真如此,#5159 落地后应该早就被发现,所以我更倾向 1;但「早该被发现」不是证据。
复现方向(给接手的人)
起一套栈(AGENTS.md「每个 agent 独立测试栈」),在某对象上存一条带 filter 的列表视图,打开它,看网络面板里 $filter 的实际载荷与返回行数:是 [{"field":...}] 且结果正确,还是 400 / 未筛选。
相关
发现于 #3419(把 ObjectDataPage「Save as view」的 URL 下钻三元组折成 spec rule)。不在该 PR 的 fence 内,也没有在真后端上验证过,故按 observation 记录,不带
pm:queue。观察
已保存视图的
filter落盘形状是 spec 的ViewFilterRule[]({ field, operator, value })—— objectstack#5159 的修法就是让persistViewFilter写folded.rules。读回来时:ObjectView.tsx(约 1347 行)const base = viewDef.filter ?? listSchema.filter,再[...baseArr, ...urlFilters]—— 这里会把 rule 对象和 URL 三元组混在同一个数组里;ListView的schema.filter,ListView.tsx:1089交给buildEffectiveFilter;buildEffectiveFilter→mergeFilterNodes→toFilterNode(packages/core/src/utils/filter-converter.ts:199),而toFilterNode对数组是原样返回:也就是说
[{ field: 'stage', operator: 'equals', value: 'open' }]会被当成一个 FilterNode 直接进$filter。客户端这一路没有任何地方把 rule 对象折成[field, op, value](ListView.mapOperator只在convertFilterGroupToAST里被调用,处理的是 FilterBuilder 的 group,不覆盖schema.filter)。值得注意的是
mergeFilterNodes自己的注释就警告过这个形状:注释是针对 spread 写的,但
toFilterNode的原样返回把同一个数组整体交出去,落到$filter上是同一类载荷。为什么标成 observation 而不是缺陷
两种可能我没能区分,需要一个跑起来的后端才能定:
ViewFilterRule[]—— 那这条只是注释与实现的语气不一致,顺手补一句说明即可;复现方向(给接手的人)
起一套栈(AGENTS.md「每个 agent 独立测试栈」),在某对象上存一条带 filter 的列表视图,打开它,看网络面板里
$filter的实际载荷与返回行数:是[{"field":...}]且结果正确,还是 400 / 未筛选。相关
isFilterAST的 400)show*与userActions,不是 filter 形状