Skip to content

driver-memory analytics 面:同一字段上的多个算子互相覆盖,{qty: {$gte:150, $lte:250}} 只剩最后一个 —— 区间塌成单边,结果被放大 #5440

Description

@os-zhuang

做 #5374($notContains 编译成裸 {$not: 'x'})时,为了确认修复没有把 $match 的组装弄坏,逐个实测了「一个 where 里同一字段带多个算子」会编译成什么。这一条不属于 #5374 的范围面 —— #5374 裁的是算子 → 谓词这一层(映射指向了错的目标),这一条在谓词 → $match 的组装这一层,#5374 修好之后依然完整存在。按 Prime Directive #10 单独记在这里,unassigned。

位置

packages/plugins/driver-memory/src/memory-analytics.ts,query() Stage 1 的 $match 组装循环(约 :180):

for (const filter of normalizedFilters) {
  const fieldPath = this.resolveFieldPath(cube, filter.member);
  matchStage[fieldPath] = /* 该条目的谓词 */;   // ← 直接赋值,不合并
}

flattenFilterCondition 对 {field: {$op1: a, $op2: b}} 会 push 两条 NormalizedCubeFilter,member 都是 field。两条都以 matchStage[fieldPath] = … 落盘,于是后一条覆盖前一条,前一个算子彻底消失。

现象(实测)

3 行数据,qty 分别为 100 / 200 / 300(id 分别为 1/2/3)。左列是 find(),右列是 analytics 面 —— 两列都是在 #5374 修复之后测的,即这条与那条无关:

where find() analytics 实际编译出的 $match
{qty: {$gte: 150, $lte: 250}} [2] [1,2] {qty: {$lt: 250}}
{qty: {$ne: 300, $gt: 150}} [2] [2,3] {qty: {$gt: 150}}

方向是放大:丢掉的永远是一个约束,所以行只会变多,不会变少。这与 #5345 / #5373 / #5374 是同一个方向、同一类后果 —— 一个看起来正常的图表统计了它本该排除的行 —— 只是机制换成了「谓词被生成了,但被下一个谓词覆盖掉」。

为什么是 bug

  1. {$gte, $lte} 是区间的规范写法。 AnalyticsQuery.where 是 FilterCondition,而 dashboard widget 元数据直接用这个形状表达「金额在某区间」「日期在某窗口」。这恰恰是最常见的多算子写法,不是边角。
  2. declared ≠ enforced。 ANALYTICS_FILTER_CAPABILITIES 声明本面支持这 11 个算子,assertFilterConditionShape 也放行了 {$gte, $lte} 这个形状 —— 声明能算,实际只算了一半。
  3. 另一半是对的。 live query path 的 normalizeFieldOperators 把同字段的多个算子合并进一个对象,多个 regex 型算子还会通过 _multiRegex 提升成 $and。所以这是同一个 driver 的两面对同一个 where 给出不同行集 —— { field: {} }(零个操作符的字段约束)在同仓有三个答案:driver-sql 组合子内 TRUE、顶层抛 INVALID_FILTER、formula/driver-memory FALSE #5240 为本包立下的不变量所禁止的形状。

建议(不代裁决)

不是一行改。合并本身容易(matchStage[fieldPath] = {...existing, ...predicate}),但有两处必须一起想清楚,否则会把一个覆盖换成另一个覆盖:

另一条路是拒收同字段多算子(与 #5345 同款),但那会拒掉本面 find() 明明能算、且是 widget 常用形状的查询,恐怕不是想要的答案。

未验证的部分

关联:#5374(同文件、同一轮核对里发现;它裁的是算子→谓词层,本条是谓词→$match 组装层)、#5345、#5373、#5240(本包两面不得分叉)、#3948(no-silent-drop)。

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions