Skip to content

Temporal filters and granularity can diverge from the chart x-axis when multiple datetime columns exist #39681

@ishimasar

Description

@ishimasar

Bug description

Summary

Charts backed by datasets with multiple datetime columns can end up with time_range / from_dttmto_dttm and granularity driven by the wrong column when dashboard native filters apply TEMPORAL_RANGE to a non–x-axis datetime (or when several temporal filters are present).

That yields queries where the visible x-axis does not match the time bounds or grouping actually used.

Root cause (high level)

  1. QueryObjectFactory._process_time_range could fall back to the first TEMPORAL_RANGE clause even when the chart’s BASE_AXIS was a different datetime column, so another column’s bounds could become the main time_range.
  2. QueryContextFactory could let granularity be inferred from dashboard context in a way that rewrote grouping / series away from the x-axis temporal column, and could strip or overwrite temporal filters in ways that dropped legitimate secondary datetime filters.

Intended behavior

  • Prefer TEMPORAL_RANGE on the chart x-axis (BASE_AXIS) when resolving time_range.
  • If the x-axis is known but no TEMPORAL_RANGE targets that axis, do not adopt another column’s TEMPORAL_RANGE as the main chart time range.
  • When the x-axis is a temporal column, pin granularity to that x-axis column so grouping stays aligned with the axis.
  • When deduplicating temporal filters for the main axis, preserve TEMPORAL_RANGE on other datetime columns (secondary time dimensions).
  • When propagating time_range into existing filters, only update the TEMPORAL_RANGE row that matches the active granularity, and compare filter col values whether they are plain strings or adhoc column dicts (sqlExpression / label).

Implementation reference

  • superset/common/query_object_factory.py(vscode-file://vscode-app/Applications/Cursor.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/superset/common/query_object_factory.py): x-axis–aware _process_time_range, adhoc-safe column matching.
  • superset/common/query_context_factory.py(vscode-file://vscode-app/Applications/Cursor.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/superset/common/query_context_factory.py): lock granularity to x-axis temporal column, narrow filter removal / time_range application, adhoc-safe column matching.
  • tests/unit_tests/common/test_query_object_factory.py(vscode-file://vscode-app/Applications/Cursor.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench/tests/unit_tests/common/test_query_object_factory.py): unit tests for _process_time_range (no wrong fallback, legacy fallback when no axis, multi-filter preference, adhoc col dict).

Commits

  • fix: align granularity with x-axis temporal column and preserve secondary dttm filters
  • test: cover QueryObjectFactory._process_time_range x-axis selection

How to reproduce (click-by-click template)

Fill in the bracketed placeholders for your environment. Remove steps that do not apply (e.g. if you reproduce via API only).

  1. Dataset: Create or use a virtual / physical dataset with at least two datetime columns, e.g. [ColumnA_dttm] and [ColumnB_dttm] (replace with real names).
  2. Chart
  • Create a time-series (or mixed) chart that uses [ColumnA_dttm] as the x-axis / time column (BASE_AXIS).
    -Save the chart as [Chart name].
  1. Dashboard
    -Create a dashboard [Dashboard name] and add [Chart name].
  2. Native filter (critical)
  • Add a dashboard native filter of type Time column / Time range (wording depends on Superset version) scoped to [ColumnB_dttm] (the non–x-axis datetime).
  • Set an explicit range, e.g. [Start] to [End] that is narrower or different from any range you might set on ColumnA.
  1. Observe
  • With no separate native filter on [ColumnA_dttm], open the chart on the dashboard.
  • Note: axis label / grain, data span, and SQL or query payload (if available) for time_range, granularity, and filters.
  1. Expected
  • Main query time bounds and grouping should follow [ColumnA_dttm] (the chart x-axis), not [ColumnB_dttm]’s dashboard filter.
    Filters on [ColumnB_dttm] should still apply as secondary temporal constraints where applicable, without replacing the x-axis time range.
  1. Actual (before fix)
  • Describe what you see, e.g.:
    • Query uses [ColumnB_dttm] range for from_dttm / to_dttm, or
    • granularity / GROUP BY does not match [ColumnA_dttm] on the x-axis, or
    • Secondary temporal filter incorrectly removed.
  1. Environment (fill in)
  • Superset version: [e.g. 4.x.x]
  • Chart plugin / viz type: [e.g. ECharts Timeseries]
  • Database engine: [e.g. Postgres / BigQuery / …]
  • Feature flags (if any): [list]
  • Browser: [e.g. Chrome 123]

Verification checklist (for reviewers)

  • Multiple datetime columns on one dataset.
  • Native TEMPORAL_RANGE only on non–x-axis column.
  • X-axis remains correct; secondary datetime filter behavior is sensible.
  • Adhoc x-axis / filter column definitions still match correctly.

Screenshots/recordings

No response

Superset version

master / latest-dev

Python version

3.11

Node version

18 or greater

Browser

Chrome

Additional context

No response

Checklist

  • I have searched Superset docs and Slack and didn't find a solution to my problem.
  • I have searched the GitHub issue tracker and didn't find a similar bug report.
  • I have checked Superset's logs for errors and if I found a relevant Python stacktrace, I included it here as text in the "additional context" section.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dashboard:native-filtersRelated to the native filters of the Dashboardexplore:timeRelated to the time filters in Explorevalidation:requiredA committer should validate the issue

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions