Skip to content

[Decision] what 「is empty」 means on a text column and on a multi-value column: null only (the spec's lowering today), or null OR '' / [] — three objectui builders disagree, and a stored sharing rule's rows depend on the answer #20311

Description

@objectstack-fleet

Unblocked: 5865751001 · #20399 ruled A (batch #234 item 1, 5865693155) · 2026-09-28T07:53Z
Ruled: 5861435168 · letter B · 2026-09-28T00:55Z

Unblocked by ruling 5865693155 on #20399 (A, batch #234 item 1; note 5865751001) — was blocked by #20399 from 2026-09-28T06:54Z. Execution: declare $empty per the ruling.

Path: permissions that actually hold | 缺项 (no item authors 「is empty」 on a sharing-rule criterion) | P2

Filing gate: ② a decision only the maintainer can make: it is the meaning of a declared operator in @objectstack/spec, and changing it re-scopes stored rules. Filed and graded by the triage seat (objectstack-wide, seat post #6015, session_01W89enF2dYV7K4N2Fbfj33f) while answering pm:retriage on objectstack-ai/objectui#10813, whose stop valve tripped. ⛔ Not a claim.

Grade: needs-user-decision · priority:p2 · domain:spec · area:records.

维护者速读

事情:「为空」这个筛选条件,在平台里有三种意思。

  • 协议(spec)把它翻成「值是 null 或缺失」。
  • objectui 的数据集桥写成「字段不存在」。
  • 最常用的那个筛选控件(共享规则、汇总字段的过滤都用它)写成「null 或空字符串」。

同一个条件,在不同地方挑出的记录不一样。

为什么要你拍板:开发实测过,清空的文本框存的是空字符串 '',不是 null。平台底层有一条测试专门钉住「空字符串不能变成 null」。所以如果照协议现在的写法(只认 null),用户眼里空着的文本记录,「为空」反而筛不出来。而改控件的写法,会改变已存共享规则能看到的记录。这是协议语义加存量数据的决定,席位不能自己选。

⭐ 数值、日期、布尔字段里存了 '',是写入环节的缺陷,不需要你裁,已单独立卡 #20308,在写入口统一转成 null。本卡只问文本字段和多值字段。

选项:

  • A:「为空」只认 null。写入口把文本的 '' 也转成 null,并迁移存量数据。代价:推翻那条「空字符串不能变成 null」的底层约定,还要做数据迁移。
  • B(荐):协议声明「为空」按字段类型展开:文本类 = null 或 '';多值类(多选、标签、多值查找)= null 或 [];其它类型 = null。三个构建器都改成读协议的这一个展开。代价:协议里多一张按类型展开的表,但只写这一处。
  • C:不动,把三种意思写进文档。

席位意见:荐 B。

  • 用户眼里「空着」就是空。主流平台也这么处理:Salesforce 把空白文本存成 null,ServiceNow、Airtable 的「为空」都匹配空字符串(⚠️ 本席未重新实测这三家)。
  • B 不动存储,不需要迁移,也保住了「空字符串不能变成 null」的底层约定。
  • 已测到零条存量规则会因为 B 改变结果(objectui、objectstack 都是 0 条;生产环境读不到)。

⚠️ 没测的:生产环境里有没有共享规则用了「为空」,读不到。

你要做的:回一个字母 —— A / B / C。

os-decision-facets

  • ① 项目长远合理性:一个操作符在协议里只该有一个意思,并且只写在一处。B 把「为空」按类型的展开写进协议,三个构建器改成读它,以后不会再出现第四种意思。A 也只剩一个意思,但要推翻已钉住的存储约定。C 让分歧永久存在。
  • ② 实际业务拉动:共享规则和汇总字段过滤用的正是那个控件。今天「为空」在多值字段上还会直接报错:控件写出 $in: [''],而 SQL 驱动拒绝对 JSON 列做 $in。这是真实的、会报错的路径,不是理论问题。
  • ③ 防 AI 写代码/写元数据犯错:三种意思意味着 AI 在不同构建器里写同一个条件,得到不同的记录集。B 让协议自己回答「为空」是什么,构建器只照读。
  • ④ 创业阶段不扩散:B 不是新能力,是给已声明的操作符补上它缺的类型展开。A 需要一次数据迁移,是三者里最重的。

Governing text:

  • packages/spec/src/data/filter.zod.ts:2307-2308 ('is_empty': '$null', 'is_not_empty': '$null');
  • the spec's value round-trip fixture VALUE_ROUNDTRIP_CASES row str_empty (「empty string must not become null」);
  • objectui#10813's measurements (dev report 5858799523, review 5859057830, release 5859079825).

After the ruling

Refs: objectstack-ai/objectui#10813 · objectstack-ai/objectui#10790 · PR objectstack-ai/objectui#10834 (closed).

Activity

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions