Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

UserFilters 的私有 specOperatorToAst 把 not_in/nin 译成 not in(带空格),服务端 400;且它是第二张手工算子表 #3470

Closed

Description

@yinlianghui

发现于 #3431 的读路径排查(该 PR 只动 packages/core/src/utils/filter-converter.ts,本条越界,单独记录)。

位置

packages/plugin-list/src/UserFilters.tsx,specOperatorToAst()(约 105 行),被 normalizeTabPresets() 用来把 spec ViewTab.filter 的 ViewFilterRule[] 折成 ObjectQL 三元组:

case 'not_in': case 'nin': return 'not in';

'not in'(中间带空格)不在 @objectstack/spec/data 的 VALID_AST_OPERATORS 里 —— 该集合有 not_in / notin / nin,没有 not in。于是 isFilterAST 判否,协议层直接 400。

实证(真实后端,@objectstack/*@17.0.0-rc.2 + app-showcase)

GET /api/v1/data/showcase_task?$filter=[["status","not in",["done"]]]
  -> HTTP 400 {"error":"Request failed","code":"INVALID_FILTER","object":"showcase_task"}

GET /api/v1/data/showcase_task?$filter=[["status","not_in",["done"]]]
  -> HTTP 200(正常返回)

即:任何作者在 userFilters.tabs[].filter / ViewTab.filter 里写 operator: 'not_in'(spec 的 VIEW_FILTER_OPERATORS canonical 拼法之一)或历史拼法 nin,该 tab 一点就是空列表 + 400。showcase / CRM 现有 metadata 都没用到 not_in,所以线上没人踩到 —— 但这是作者面上一个正常拼法,不是内部值。

更根本的一层:这是第二张手工算子表

specOperatorToAst 与 spec 自己导出的 normalizeFilterOperator(@objectstack/spec/ui)职责重叠。写入侧 app-shell/src/views/viewFilterFold.ts 已经明确改成只走 spec 的这一个出口,并在注释里写清了原因:Studio inspector 里那张私有表曾经比 builder 的下拉落后四个算子。specOperatorToAst 就是同一类私有表的又一份,not in 正是它已经漂出去的证据(另外 ends_with / contains / between 等只靠 default: return op 兜底,能过纯属巧合)。

顺带说明为什么 canonical 表其实不需要存在:spec 的 19 个 VIEW_FILTER_OPERATORS 全部已经是 VALID_AST_OPERATORS 的成员,所以 rule → AST 的下降是纯结构性的,算子只需过一遍 normalizeFilterOperator 即可,不需要任何映射表。#3431 的 core 侧折叠就是这么做的。

建议方向(不预设结论,交 PM/维护者裁决)

把 normalizeTabPresets 里的下降改为复用 @objectstack/spec/ui 的 normalizeFilterOperator(与写入侧、与 #3431 的读侧同一个出口),删掉 specOperatorToAst;spec 不认识的拼法原样透传,让服务端 400 大声拒绝,而不是被 default 悄悄放行成别的东西。

需要留意的一处差异:现表把 before/after 映射成 </>,而 before/after 本身也在 VALID_AST_OPERATORS 里 —— 改成透传前要确认服务端对这两个算子的日期语义与 </> 一致,否则是行为变更而不是纯修复。

相关:#3431(读路径的 ViewFilterRule[] 折叠)、objectstack#5159(写入侧折叠)、objectstack#4121(数组型 $filter 的 400)。

Activity

  1. self-assigned this
    on Aug 6, 2026
  2. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    ContributorAuthor

    认领:objectui 分片 PM 并行批第 52 单
    会话:session_01GTRjn8xBqp75dk7kFupVRt
    分支:claude/issue-3470-userfilters-operator-table
    Worktree:objectui-issue-3470
    文件面:packages/plugin-list/src/UserFilters.tsx + 该包测试 + changeset。

    PM 裁定(issue 交裁决的方向):采建议——normalizeTabPresets 复用 spec 的 normalizeFilterOperator 单一出口、删 specOperatorToAst 私有表;未知拼法透传让服务端响亮 400。before/after 差异点按 issue 自己的警示处理:先实证服务端对 before/after 的日期语义与 </> 是否一致(可复用 #3431 的独立测试栈方法),一致则透传,不一致则该两键单独保留映射并注明原因——行为变更与纯修复的边界以实测为准。


    Generated by Claude Code

  3. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    ContributorAuthor

    验收:ACCEPT → PR #3483(并行批第 52 单,会话 session_01GTRjn8xBqp75dk7kFupVRt),18/18 检查落定全绿。ready + auto-merge 因 GraphQL 配额排队,恢复即执行。

    复核要点:

    1. 裁定的测量门执行到位:before/after vs </> 在 date 与 datetime 字段双向实测,id 集比对而非计数——判 IDENTICAL,纯透传落地,边界落在纯修复侧;整表 19 算子对拍 18/19 相同、唯 not_in 从 400 转 200,精确锁定缺陷面。
    2. case undefined → '=' 的处置认同:issue 未提及的这个子案,旧行为是把「作者没写 operator」静默发明成等值谓词——spec 的 z.enum 无默认值,发布侧本就拒收,消费端的 = 是 #0.1 代偿。改为响亮 400 正确;单独成 commit、独立测试钉双面事实、PR 专节披露并给出撤回路径——处置形式同样标准。
    3. 反向验证 41/22 按预测分裂(留绿的 22 条恰是「本就该不动」的契约面);离线锚定用 spec 自己的 isFilterAST([finding] 已保存视图的 ViewFilterRule[] 被原样当 FilterNode 送进 $filter,未在客户端折成 AST #3431 同式);fixture 按消费半径重拼写而非替换。
    4. 顺带发现 objectstack#5869(标量集合值 500 而非 ADR-0112 包络 400)按跨车道纪律立单不修,与 [Decision] schema.onNavigate is a function value read off the grid schema — the fifth key #5091's ruling did not cover #5234 的区分论证写入正文——正确。

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions