Skip to content

analytics: ObjectQLStrategy 静默忽略 timeDimensions[].dateRange —— 恰好是 date-granularity 图表必走的那条路 #3650

Description

@os-zhuang

#3602 的第 3 项(让 ObjectQLStrategy.generateSql 预览与实际执行相符)里发现的相邻缺口。为了让预览"忠实",我需要确认 execute() 到底施加了哪些谓词 —— 结果发现它根本不读 dateRange

现象

ObjectQLStrategy.execute() 只把 query.where 送进 engine:

// objectql-strategy.ts — timeDimensions 只被读了 granularity
for (const td of query.timeDimensions ?? []) {
  if (td.granularity) granByDim.set(td.dimension, td.granularity);   // ← 只有 granularity
}
...
const filter = {};                                   // ← 只来自 normalizeAnalyticsFilters(query)

normalizeAnalyticsFilters 只读 query.where(filter-normalizer.tsnormalizeAnalyticsFilters),dateRange 从来不在 where 里 —— dataset-executor.tsbuildQueryselection.timeDimensions 原样透传成 q.timeDimensions,q.where 是独立的另一支。

对比另外两条路,它们都老老实实处理了:

  • NativeSQLStrategy:td.dateRangecol BETWEEN $n AND $n+1(还顺带做了 SQLite epoch 的 storage 强转)。
  • preview-evaluator.ts:// 1. Row-level filters: where, then timeDimension dateRanges.

只有 ObjectQL 这条路把它丢了。

为什么这个特别值得修

不是"少数驱动才会踩"的边角:dateGranularity 的查询会强制走到这条路上NativeSQLStrategy.canHandle 对任何携带 granularity 的查询直接 decline,于是即便在 Postgres/SQLite 这种完全支持原生 SQL 的部署上,一个按天/周/月分桶的趋势图也会落到 ObjectQLStrategy。

而"按时间分桶的趋势图"恰恰是最可能同时带 dateRange 的查询形态(近 12 个月、本季度……)。所以实际后果是:

一个限定了时间范围的趋势图,在任意驱动上都会画出全量历史,而不是所选区间。

没有任何报错,图表看上去正常,只是数字不对 —— 静默的错误结果比抛异常更难被发现。

不是越权

dateRange 是展示性过滤,不是安全边界;read scope 与 ExecutionContext 两条带子(#3601 / #3602)都不受影响。这是正确性问题,不是泄露。

落点

objectql-strategy.tsexecute(),把 timeDimensions[].dateRange 编进送往 engine.aggregatefilter,大致对应 NativeSQLStrategy 那段:

for (const td of query.timeDimensions ?? []) {
  if (!td.dateRange) continue;
  const range = Array.isArray(td.dateRange) ? td.dateRange : [td.dateRange, td.dateRange];
  // → filter[field] = { $gte: range[0], $lte: range[1] }  (与既有同字段合并逻辑一致,勿覆盖)
}

两处要当心:

  1. 同字段多操作符的合并execute() 里已有的 merge 逻辑({...existing, ...converted})是为 {$gte,$lte} range 准备的 —— dateRange 必须走同一条合并路径,否则会和用户自己写的 where 里同字段条件互相覆盖。
  2. 值的存储形态。NativeSQL 走 coerceTemporal 是因为它绕过了驱动的 CRUD 强转;ObjectQL 经 engine.aggregate 应当由驱动自己处理,但需要验证 —— SQLite 的 Field.datetime 存 epoch 整数,ISO 字符串比较是 affinity 陷阱(参见 native-sql-strategy 里那段注释描述的 "No rows" bug)。

修完后 #3602 第 3 项里 generateSql 那段"故意不渲染 BETWEEN"的注释需要同步更新 —— 届时预览应当把 dateRange 一起渲染出来。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions