Repository navigation
Description
修 #5325 时把 worktree 同步到含 #5329(#5158 拍板 C 第 2 步)的 origin/main,核对本次改动
是否与之冲突,发现 analytics 这条路径不在那一单的门面清单里。不属于 #5325 的范围面,
按 Prime Directive #10 单独记在这里,unassigned。
位置
packages/services/service-analytics/src/strategies/filter-normalizer.ts 的
normalizeAnalyticsFilterTree:
const where = (query as { where?: unknown }).where;
if (!where || typeof where !== 'object' || Array.isArray(where)) return null;Array.isArray(where) → return null —— 整条 where 消失,不报错,不留痕。
现象(实测)
NativeSQLStrategy.generateSql({ cube:'deals', measures:['total'], dimensions:['id'], where: [{ stage: 'won' }] }) 生成:
SELECT id AS "id", COUNT(*) AS "total" FROM "deal" GROUP BY id -- 没有 WHERE
params 为空。作者写了筛选,图上画的是整个数据集 —— #3650 / #4128 反复付过学费的那一类
静默放宽,只是入口换成了数组写法。
为什么现在值得单独记
#5329(#5158 拍板 C 第 2 步)刚刚把 FilterArray 定成 INPUT-ONLY 的编写期糖,并让
engine 的六个入口(find/findOne/count/aggregate/update/delete)统一走
isFilterAST → parseFilterAST 下沉,四个驱动的数组方言删除、改成响亮的
INVALID_FILTER / 400;cloud 的 RemoteTransport.buildWhereSQL 自 cloud#1075 起也是拒收。
那一单的结论是「一个 query 在哪条路上都只有一个答案」。
analytics 是第五道门:它自己就把 where 编译成 SQL(NativeSQLStrategy)或
FilterCondition(ObjectQLStrategy),不经过 engine 的那道下沉。今天它给的是三个可能答案
里最差的那个 —— 既不是 lower(照 parseFilterAST 解释),也不是 refuse(响亮 400),
而是静默丢弃。
两条路都行,需要拍板
- Lower —— 与 fix(objectql,driver-sql,driver-memory,driver-mongodb)!:
FilterArray在 engine 门下沉,四驱动数组方言删除 (#5158 拍板 C 第 2 步) #5329 的 engine 入口一致:isFilterAST(where)就先parseFilterAST
再交给buildNode。作者用哪种写法都得到同一份行集,与「FilterArray 是编写期糖」
的定性完全吻合。代价是service-analytics目前只依赖@objectstack/core+
@objectstack/spec,要看parseFilterAST住在哪个包、会不会带进新依赖。 - Refuse —— 与四个驱动删除数组方言后的姿态一致,抛错而不是静默丢。实现最省,
且分析查询的where通常由 dashboard metadata 提供,编写期炸掉比运行期多画几万行好。
个人倾向 1(lower):#5158 的拍板 C 说的就是「在门口下沉」,analytics 只是当时没被点到
的一道门;拒收会让同一份 dashboard metadata 在普通 find() 上能跑、在图表上报错。
但这是契约位置的选择,不该由实现顺手定。
未验证
没有找到今天真的会给 analytics 传数组 where 的 producer(dashboard widget 的 filter
走 FilterCondition 对象形状);这条是面的漏洞,不一定有现网触发。严重度请 PM 按 triage
定,不代表我判断它低 —— #5158 那条路上的同类问题一开始也没有已知 producer。
关联:#5158(拍板 C)、#5329(第 2 步,engine 六入口下沉 + 四驱动删方言)、
cloud#1075(transport 侧早已拒收)、#5325(同函数、同一轮里发现)、#3650 / #4128
(同类「谓词静默消失 → 画全表」)。
Activity
os-zhuang commentedon Aug 5, 2026
维护者拍板:取 1(lower) —— 认领 + 派发(engine/services 车道 PM,会话
session_01Pbu27iNUfQCHeuS551Rqo7)分支
claude/issue-5334-analytics-lower-filter-array,专用 worktree/home/user/objectstack-issue-5334,域domain:services,标target:v17(收紧类,窗口在 GA 前)。裁定理由
正文倾向 lower,维护者同意。补两条正文没有的:
一、lower 的唯一成本经核实不存在****。正文写「代价是
service-analytics目前只依赖@objectstack/core+@objectstack/spec,要看parseFilterAST住在哪个包、会不会带进新依赖」。实测:parseFilterAST就在packages/spec/src/data/filter.zod.ts,而service-analytics/package.json的dependencies里已经有@objectstack/spec—— 零新依赖。这条顾虑可以划掉,lower 与 refuse 之间不再有成本差。二、refuse 会制造一个新的跨面分叉,而这一族的全部工作正是在消灭跨面分叉。同一份 dashboard metadata 里的
where,走普通find()经 engine 门下沉后能跑(#5329 已落地),走图表却报错 —— 那正是 #5158 拍板 C 要关掉的形状,只是换了两条路来比。已定范围
isFilterAST(where)为真 →parseFilterAST下沉后交给buildNode,与 #5329 的 engine 六入口逐条同构:[]→ 无过滤(engine 门的做法是删键);isFilterAST为真 → 下沉;[condA, 'or', condB])parseFilterAST表达不了、FilterArraySchema也不接受,它必须落进拒收分支 —— 这一点 fix(objectql,driver-sql,driver-memory,driver-mongodb)!:FilterArray在 engine 门下沉,四驱动数组方言删除 (#5158 拍板 C 第 2 步) #5329 已查明并写在其 PR 里。拒收的错误信封与措辞请与 #5329 在四驱动落的
filterArrayReachedDriverError对齐(ADR-0112 /INVALID_FILTER),不要另造第三套。filter-normalizer.ts几十分钟前刚被 #5325 / PR #5335 大改(已合入1792384e8):nullSafeNegationOperand守卫下推、NormalizedFilterNode新增{kind:'const'}、三个编译器的paramBase/joinBase截断、{$in:[]}与{a:{}}的新答案。worktree 必须基于1792384e8或更新,并逐条重核 issue 引用的normalizeAnalyticsFilterTree那两行是否仍成立。明确不在范围
packages/spec/**—— 只消费parseFilterAST/isFilterAST,不改。packages/objectql/**、packages/plugins/**—— fix(objectql,driver-sql,driver-memory,driver-mongodb)!:FilterArray在 engine 门下沉,四驱动数组方言删除 (#5158 拍板 C 第 2 步) #5329 已落地,只读作参照。{field: {$eq: null}}/{$ne: null}编译成col = ''/col != '',与同文件里{field: null}的IS NULL自相矛盾 #5332({$eq: null}编译成col = '')、/analytics/sql回显的 SQL 丢掉$startsWith/$endsWith谓词:回显比实际执行的查询更宽,无法复现结果 #5333(回显丢$startsWith/$endsWith)—— 同文件的独立单,本单不碰,发现池待分诊。content/docs/releases/**。Generated by Claude Code
fix(service-analytics): lower a `where` FilterArray instead of droppi…
fix(analytics,rest): an analytics filter refusal reaches the caller a…
fix(service-analytics): gate `where` source fields — a filter over a …
fix(service-analytics): lower the `where` before seeding an ad-hoc cu…
docs(spec): the element-number D3 entry names the route hop — arrays …
docs(spec): the element-number D3 entry names the route hop — arrays …
fix(driver-memory): the cube face lowers the FilterArray spelling of …