Skip to content

fix(driver-mongodb): 空 $and/$or/$not 归约成布尔单位元,非 filter 节点先响亮拒收 (#5239) - #5323

Draft
os-zhuang wants to merge 1 commit into
mainfrom
claude/issue-5239-filter-logic-identity-batch
Draft

fix(driver-mongodb): 空 $and/$or/$not 归约成布尔单位元,非 filter 节点先响亮拒收 (#5239)#5323
os-zhuang wants to merge 1 commit into
mainfrom
claude/issue-5239-filter-logic-identity-batch

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5239
同批携带 #5146 spec 半边(已落地)与 #5240 spec 半边(受限携带 → 实测后不进表)

⚠️ 本 PR 交付的是 #5239 的硬约束半边(driver-mongodb 归约),四条一致性表行没有加进去 —— 前提复核推翻了「只差 mongodb 一家」这条。理由与实测在下面第 2 节,已另立 #5322 上交拍板。请连同该单一起验收。


1. 前提复核(先做的事)

按仓规对 origin/main(175d789)逐后端实测四条 case,而不是照抄 issue 正文。实测方法本身有阳性对照:我的 driver-sql 探针复现了 PR #5296 / #5243 已落地 pin 的逐字结果({$not:{stage:'won'}} → 行 2,3,4;{$or:[]} → 零行),说明测的是真行为不是我的假设。

后端 {$and:[]} {$or:[]} {$or:[{a},{}]} {$not:{}}
期望 全部行 零行 全部行 零行
formula 全部行 零行 全部行 零行
driver-memory 全部行 零行 全部行 零行
driver-sql(#5134/PR #5243) 全部行 零行 全部行 零行
driver-sqlite-wasm 全部行 零行 全部行 零行
driver-mongodb(改前) 服务端报错 服务端报错 全部行 零行
read-scope-sql 抛错 抛错 行 1,2 整表
analytics filter-normalizer 抛错 抛错 行 1,2 整表

issue 正文点名的「四个后端」里就含 read-scope-sql,而它和同包的 filter-normalizer 各红四格。两者都在册跑全量 FILTER_LOGIC_CASES(read-scope-sql-conformance.test.tsnative-sql-filter-logic-conformance.test.ts),所以四条一进表就是八格红。

2. 为什么四条没进表

那两处不是「没跟上」,是成文的相反立场:filter-normalizer.ts 的错误消息逐字写着「An empty combinator has no defensible reading — dropping it widens the query, and treating it as "match nothing" silently empties a chart」,而「match nothing」正是 #5134$or: [] 定的答案;read-scope-sql.test.ts:86 还用 toThrowError(/non-empty array/) 把这个抛错钉住了。

「响亮拒收」本身是有先例的答案 —— #5240{ field: {} } 取的就是它。所以这是两条已落地立场必须有一条让步,属公开契约定调,不是实现细节。已立 #5322(含两轴分析与推荐:取单位元 + 可选地在 publish/lint 面对字面量空组合子拒收,这样运行期语义单一而作者错误仍在编写期爆炸)。派发词的硬要求是「批次 PR 必须对自己的内容全绿」,所以红行不进表,实测矩阵原样留在 filter-logic-conformance.ts 里,下一位不必重测。

同理另两族也没进表:

3. 本 PR 实际改了什么

driver-mongodb(#5239 的硬约束半边)

translateFilter 原样透传组合子数组,而 MongoDB 对空数组既不答 TRUE 也不答 FALSE,是第三种行为:拒绝整条查询。改成与 #5134 同一套结构性三值归约:先把整棵树判成 true / false / clause,再据此产出 —— 而不是「编译完再问有没有产出条件」,后者分不清「本来就是空」和「有东西没编译出来」。

  • $and → TRUE,不产出条件。
  • $or → FALSE,产出真实的零行条件 { _id: { $in: [] } }。这一格是关键:「什么都不产出」等于 {},而 find / updateMany / deleteMany{} 读作全部文档,方向正好相反。
  • {}$or 分支仍是 TRUE 析取项,{$not: {}} 仍是零行 —— 这两条 MongoDB 本来就与布尔代数一致,所以归约按结构做而不是只判 length === 0(只判长度会有一半是蒙对的)。
  • 发出的每个 $and / $or 数组因此都保证非空(丢掉单位元成员 ≠ 发一个空数组)。

形状拒收在同一改动里,且先于归约。单位元把「这个节点没有谓词」读作「匹配全部文档」,所以空节点必须只有一个成因。改前实测,这一格比 driver-sql 当年更糟 —— 不是「被静默忽略」,是已经在放宽:

输入 改前译文 含义
{ $or: [new Date()] } { $or: [{}] } 每一份文档
{ $or: 'x' } {} 每一份文档
{ $not: null } {} 每一份文档
{ $or: ['x'] } { $or: [{ '0': 'x' }] } 一个没有文档拥有的字段

updateMany / deleteMany 走同一个 translate 层,在那里「放宽到全部文档」不是行数不对而是数据丢失。现按 ADR-0112 以 INVALID_FILTER / status: 400 拒收并点出位置(filter.$or[0]),消息不带 [mongodb] 前缀(#3867)。判定按原型而非 typeof —— Date / RegExp / class 实例都满足 typeof x === 'object' 却枚举为空。

一处刻意不动:{ field: {} } 仍归为 'clause',译文逐字节不变,不替 #5240 做任何裁决。

packages/spec(仅文档,零运行时改动)

4. 验证

消费半径全跑(表钉住的每一个后端 + 本次改动包):

@objectstack/spec               309 files / 7949 tests passed
@objectstack/driver-mongodb       8 files /  169 passed | 128 skipped
@objectstack/driver-sql          60 files /  765 passed |  44 skipped
@objectstack/driver-memory       11 files /  303 passed
@objectstack/formula             15 files /  340 passed
@objectstack/driver-sqlite-wasm  16 files /  232 passed
@objectstack/service-analytics   42 files /  555 passed
typecheck (spec, driver-mongodb) 通过
check:generated                  9/9 up to date(rebase 到 26e1029 后复跑)
check:driver-conformance         4 drivers x 5 case-sets,20 covered,0 DEBT
check:nul-bytes                  OK(另对改动文件做了 `[\x00-\x08\x0b\x0c\x0e-\x1f]` 自扫,无命中)

skip 不是 pass:driver-mongodb 的 128 skipped 是需要真 mongod 的那半边 —— 本容器取不到 mongodb-memory-server 的二进制。所以新 pin 分两半,与本包既有惯例一致:译文断言永远跑(对这四条,发出的 document 就是语义:{} = 全部,{_id:{$in:[]}} = 零),真 mongod 那半边回答「服务端是否同意」,在 CI 能取到二进制时才跑。

反向验证 —— 方向先预测,再跑

预测:把 mongodb-filter.ts 还原成 origin/main,新 pin 里除「existing translation is untouched」那组控制项外全红

实测 28 红 / 9 绿,方向与预测一致。唯一偏差是我预测绿的数量写成 8 而实际 9 —— 差的那一条是 a field constrained by zero operators is still not ruled on (#5240),它本就该在还原后保持绿(它钉的正是「{stage:{}} 前后逐字节不变」),是我数自己的控制项时漏数了一条,不是方向反了。红的 28 条覆盖三组单位元、嵌套组合、以及全部 15 条形状拒收。


Generated by Claude Code

…tity, refusing non-nodes first (#5239)

`translateFilter` passed combinator arrays through verbatim, and MongoDB
answers an empty one with neither TRUE nor FALSE but a third behaviour: it
refuses the query (`$and/$or/$nor must be a nonempty array`). So `{$and: []}`
and `{$or: []}` reached find/count/updateMany/deleteMany as a server error
carrying no ADR-0112 code, while driver-sql (#5134), driver-memory and formula
all answered them as identities.

Replaced with the same STRUCTURAL three-valued reduction: reduce the whole tree
to true/false/clause first, then emit. Empty `$and` becomes TRUE (no condition);
empty `$or` becomes FALSE and emits a real zero-row condition
(`{_id: {$in: []}}`) — emitting nothing would be `{}`, which find/updateMany/
deleteMany read as EVERY document, the opposite answer. Every `$and`/`$or`
array emitted is therefore guaranteed non-empty.

Shape rejection lands in the same change and runs BEFORE any identity: measured
on main, `{$or: [new Date()]}` translated to `{$or: [{}]}` (every document) and
`{$or: 'x'}` / `{$not: null}` translated to `{}` (every document). updateMany
and deleteMany translate the same `where`, where that is data loss rather than a
wrong row count. Non-nodes now raise INVALID_FILTER / 400 naming the position;
the gate judges by PROTOTYPE, since Date/RegExp/class instances satisfy
`typeof x === 'object'` while enumerating empty.

spec is documentation only: FilterConditionSchema's contract TSDoc now states
the NULL-safe `$not` semantics ruled in #5146, and filter-logic-conformance.ts
records the measured matrix for the three ruled-but-not-yet-enrolled case
families. The four FILTER_LOGIC_CASES rows #5239 asks for are deliberately NOT
added: read-scope-sql and the analytics filter-normalizer, both enrolled
backends, refuse empty combinators fail-closed by design and pinned test, which
contradicts the identity ruling — escalated as #5322.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB
@vercel

vercel Bot commented Aug 4, 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 4, 2026 7:59pm

Request Review

@github-actions github-actions Bot added the size/l label Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/driver-mongodb, @objectstack/spec.

108 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-mongodb, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-mongodb)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/driver-mongodb, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-mongodb, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/driver-mongodb, @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/driver-mongodb, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Copy link
Copy Markdown
Contributor Author

PM 验收(session_01ErbEDVAg1No9gdg1pgDAGB)——通过

进入串行落地链链尾(当前序:#5304#5306#5308#5318#5319#5321#5314#5312 → 本 PR)。轮到时由 PM 通知重建 worktree 同步 + 全量重生成 + 兄弟断言,再翻 ready 并 arm auto-merge。


Generated by Claude Code

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 protocol:data size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[spec] FILTER_LOGIC_CASES 补空组合子的布尔单位元四条 —— 需与 driver-mongodb 的单位元归约同时落地

2 participants