Skip to content

analytics: 只用来限定「日期区间」的 timeDimension 被补上默认 dateGranularity,于是网格被静默按月拆分 —— 「按 Owner 统计」加个日期筛选就变成「按 Owner × 月」 #5688

Description

@os-zhuang

立单于 #5537 的实施过程(PR 见该单)。#5537 的 fields 装配无关:下面的复现跑在 #5537 从未影响的那条「无 measure filter 单查询」路径上,且在 origin/main(c36abfe98,含 #5587/#5634/#5667)与 #5537 的分支上逐字节同样复现。范围外,单独立单。

现象

一个 dataset selection 只把日期维度用作窗口timeDimensions: [{ dimension, dateRange }],不带 granularity,也把它列进 selection.dimensions)——这正是 dashboard 日期区间筛选器的产物,executor 自己的注释就把它称作 "a dashboard date-range filter is the usual source"——结果网格却额外按该日期维度分桶:多出一列没人选过的时间列,行数按月裂开。

复现

dataset(关键条件:日期维度声明了显式 dateGranularity):

dimensions:
  - { name: owner,      field: owner_id,   type: lookup, label: Owner }
  - { name: close_date, field: close_date, type: date,   label: Close Date, dateGranularity: month }
measures:
  - { name: opp_count,  aggregate: count,  label: Opps }

三条数据:u1 @2026-01u1 @2026-02u2 @2026-01

selection —— 只按 owner 分组,日期只当窗口:

{ dimensions: ['owner'],
  measures: ['opp_count'],
  timeDimensions: [{ dimension: 'close_date', dateRange: ['2026-01-01', '2026-02-28'] }] }

实测响应:

fields [{"name":"owner","type":"string","label":"Owner"},
        {"name":"close_date","type":"time"},          <-- 没人选过这一列
        {"name":"opp_count","type":"number","label":"Opps"}]
rows   [{"owner":"u1","close_date":"2026-01","opp_count":1},
        {"owner":"u1","close_date":"2026-02","opp_count":1},   <-- u1 被拆成两行
        {"owner":"u2","close_date":"2026-01","opp_count":1}]

期望:2 行(u1: 2u2: 1),fieldsowner + opp_count

落点

packages/services/service-analytics/src/dataset-executor.ts buildQuery(origin/main c36abfe L888-L909):

const resolvedTimeDims = selTimeDims.map((t) => {
  if (t.granularity) return t;
  const granularity = granularityFor(t.dimension);   // L899
  return granularity ? { ...t, granularity } : t;
});

granularityForresolveDimensionGranularity(selection, name, datasetDefault)datasetDefault 即「dataset 显式声明过 dateGranularity」时编译出的单元素 cube.granularities。于是一个只有 dateRange 的条目被补上 granularity: 'month';而一旦条目带上 granularity,它就是 GROUP BY 项、并且按 #4033projectedDimensionsobjectql-strategy.ts L1130,native 侧同义)投影成结果列。

这条补默认值本身是刻意的buildQuery 上方的长注释写明了理由:compareTo 需要的正是「窗口条目不得抑制分桶」,否则主网格按月、比较网格按原始时间戳,两边维度键对不上,每个 compare 列都空。所以两种需求在同一处打架:

判据看起来应该是「这个 dimension 是否出现在 selection.dimensions(或调用方自己写了 granularity)」,但这属于会改变响应形状的契约取舍,不该由我在 #5537 里顺手猜,故立单。

触发条件与影响面

必要条件(三者同时):dataset 的该日期维度声明了显式 dateGranularity;selection 的 timeDimensions 条目granularityselection.dateGranularity 未设。HotCRM 一类 dataset 普遍声明 dateGranularity,dashboard 的日期区间筛选器又普遍只发 dateRange,因此可达性不低。

命中时是行数与列集都变(不是纯元数据问题):一个「Won by Owner」表加上日期筛选后每人一行变成每人每月一行,KPI 单值卡会拿到多行里的第一行。

附带的次要症状(同一根因,同一条路径):这样投影出来的时间列在 fields 里只有 type 没有 label —— analytics-service.ts 的维度 label 富化(L915 起)只遍历 selection.dimensions,看不到只在 timeDimensions 里的列。#5537 的 PR 把这一点当作两条路径一致的既有行为钉住了(dataset-dimension-field-descriptors.test.ts 里成对的控制用例),并明确指向本单。

不重复

已搜:timeDimensions dateGranularity(命中 #3650/#3777,均已关闭且是另一件事:前者是 dateRange 被忽略,后者是上界打在 datetime 列上)、dataset-executor buildQuery groupingdateRange granularity bucket dashboard widget rowsdate range filter extra column group by month widget —— 无 open 重复。

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions