Skip to content

[finding][spec] $between 的两个端点也不含 string —— 与 #5685 同一处矛盾,而日期宏解析器会走进数组 #6571

Description

@qq9340100

发现于 #5685(PR #6570)的实现过程。按 PD#10 单独记录,未在那个 PR 里顺手改 —— $between 是另一个算子,不在 #5685 的测量范围内。

事实

#5685 修的是 $gt/$gte/$lt/$lte 四个槽位缺 string。同一文件里 $between 的两个端点带完全相同的矛盾,且 #5685 的 PR 没有碰它:

packages/spec/src/data/filter.zod.ts —— RangeOperatorSchema(约 :125)与 FieldOperatorsSchema.$between(约 :275)都是:

$between: z.tuple([
  z.union([z.number(), z.date(), FieldReferenceSchema]),
  z.union([z.number(), z.date(), FieldReferenceSchema])
]).optional(),

即两个端点都没有 string

为什么这不是「顺带一提」

日期宏解析器会走进数组packages/core/src/utils/filter-tokens.tswalk:

if (Array.isArray(node)) return node.map(walk);

所以下面这个写法是解析器完整支持的,而且是区间过滤最自然的表达:

{ close_date: { $between: ['{current_year_start}', '{current_year_end}'] } }
  → { close_date: { $between: ['2026-01-01', '2026-12-31'] } }

解析结果的两个端点都是 string,恰好是声明拒收的类型。$between 的语义(闭区间)天然就是日期窗口的主场,所以这里的 declared ≠ practiced 比 $gt 那四个更贴近作者的真实写法。

危害面 / 为什么归为 observation-class

#5685 同源:RangeOperatorSchema 没有运行期消费者;FieldOperatorsSchema.$betweenNormalizedFilterSchema 可达,但 objectql 的读写路径与各 driver 都不拿它校验 where。所以今天不产生运行期失败 —— 危害仍是发布出去的契约在误导作者(尤其 AI 作者):读到 $between: [number | date, ...] 会得出「区间端点要传 Date 对象」的结论,而平台自己的日期宏路径只会给字符串。

严重度留给分诊轮,与 #5685 当初一样。

建议方向(未拍板)

#5685 的落点保持一致即可 —— 把 z.string() 并进两个端点的联合,理由与 PR #6570 的 docblock 同源(本 schema 是 field-agnostic 的、ISO refine 会拒掉 Field.time 声明的 HH:MM 形态、driver 侧 coerceFilterValue 已按列类型规范化端点)。若采纳,Filter< T >$between?: T[K] extends number | Date ? [T[K], T[K]] : never 的 guard 也应同步。

一并留意:RangeOperatorSchemaFieldOperatorsSchema 是同一契约的两份拼写,#5685 的 PR 已经踩过一次「只改文档面会留下可达面仍拒绝」的坑,$between 若要改也应两处同改。

相关

#5685(同一处矛盾的 $gt/$gte/$lt/$lte 半边)、PR #6570(其实现,docblock 里记录了裸 string vs ISO refine 的实测取舍)、#3810

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions