Skip to content

fix(explore): keep x-axis label when overriding Time Column with time comparison - #42875

Open
yousoph wants to merge 2 commits into
apache:masterfrom
yousoph:sc-111332-time-column-single-point
Open

fix(explore): keep x-axis label when overriding Time Column with time comparison#42875
yousoph wants to merge 2 commits into
apache:masterfrom
yousoph:sc-111332-time-column-single-point

Conversation

@yousoph

@yousoph yousoph commented Aug 7, 2026

Copy link
Copy Markdown
Member

SUMMARY

Fixes a bug where a chart with Time Comparison (a time offset such as 1 year ago) collapses into a single data point when its Time Column is overridden on a dashboard to a non-default temporal column. Reported by The Knot (Preset SC-111332).

Both dashboard entry points for the Time Column — the native "Time Column" filter and the Display Controls dropdown — emit granularity_sqla through extra_form_data, which maps to granularity and converges on a single backend method: QueryContextFactory._apply_granularity.

For an adhoc (BASE_AXIS) x-axis, that method re-points the x-axis at the overridden column but also renamed the column's label to the overridden column name:

x_axis_column["sqlExpression"] = granularity
x_axis_column["label"] = granularity   # <- the bug

The offset join in processing_time_offsets, the post-processing pivot index, and the frontend series extraction (getXAxisLabel(rawFormData) in Timeseries/transformProps.ts) all reference the x-axis by its original saved label. Renaming the label desynchronizes those consumers from the label the saved chart still advertises. With a Time Comparison offset in play, the result is keyed under a label nothing downstream recognizes, so ECharts treats the temporal column as a numeric series and the x-axis collapses to a single point (the reported symptom: a lone point with the temporal column in the legend and a y-value in the trillions — the timestamp in milliseconds).

Fix: for an adhoc x-axis, only swap the underlying sqlExpression to the overridden column and keep the original label, so the join, the pivot and the frontend keep referencing the same label. The bare-string x-axis path (which has no distinct label) still replaces the column wholesale and realigns the pivot index.

Because both override paths converge on _apply_granularity, this fixes the issue from both the Filters panel and the Display Controls.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before: overriding the Time Column with a Time Comparison configured collapses the series into a single point.
After: the series plots across the full range under the original x-axis label, with the offset (… 1 year ago) populated.

TESTING INSTRUCTIONS

  1. Create the dataset from the report (Postgres):
    WITH RECURSIVE months AS (
        SELECT CAST('2024-01-01' AS DATE) AS m_date
        UNION ALL
        SELECT CAST(m_date + INTERVAL '1 month' AS DATE) FROM months WHERE m_date < '2026-04-01'
    )
    SELECT m_date AS wedding_date,
           m_date + INTERVAL '14 days' AS purchase_date,
           (100 + EXTRACT(MONTH FROM m_date) * 10) AS revenue
    FROM months;
  2. Build a line chart with x-axis = wedding_date, metric = revenue, date range 2025-01-01 -> today, and Time Comparison 1 year ago. Save to a dashboard.
  3. On the dashboard add a Time Range and a Time Column control (or use the Display Controls Time Column dropdown). Set Time Column = purchase_date.
  4. The chart should now plot purchase_date across the range instead of collapsing to a single point.

Automated coverage: pytest tests/unit_tests/common/test_time_column_offset_repro.py drives the full offset + pivot pipeline and asserts the overridden Time Column plots multiple points under the original x-axis label. tests/unit_tests/common/test_query_context_factory.py::test_apply_granularity_with_x_axis_dict asserts the label is preserved.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
  • Introduces new feature or API
  • Removes existing feature or API

🤖 Generated with Claude Code

@dosubot dosubot Bot added the explore:time Related to the time filters in Explore label Aug 7, 2026
@bito-code-review

bito-code-review Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #1293f3

Actionable Suggestions - 0
Review Details
  • Files reviewed - 3 · Commit Range: f8ac93d..f8ac93d
    • superset/common/query_context_factory.py
    • tests/unit_tests/common/test_query_context_factory.py
    • tests/unit_tests/common/test_time_column_offset_repro.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +59 to +62
"""Build the reporter's dataset (monthly wedding/purchase dates)."""
fd, path = tempfile.mkstemp(suffix=".db")
os.close(fd)
uri = f"sqlite:///{path}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Each invocation creates a persistent named temporary database file, but the path is never removed after the test. Since both tests construct a dataset and repeated test runs reuse this helper, these files accumulate in the system temporary directory; add teardown or use a temporary-directory context that removes the database after use. [resource leak]

Severity Level: Minor 🧹
- ⚠️ Repeated test runs accumulate temporary SQLite files.
- ⚠️ Long-lived CI workers may experience temporary-disk growth.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/unit_tests/common/test_time_column_offset_repro.py
**Line:** 59:62
**Comment:**
	*Resource Leak: Each invocation creates a persistent named temporary database file, but the path is never removed after the test. Since both tests construct a dataset and repeated test runs reuse this helper, these files accumulate in the system temporary directory; add teardown or use a temporary-directory context that removes the database after use.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed. The helper now takes a path from pytest's tmp_path fixture, which is auto-removed after each test, so no temporary DB files accumulate.

Comment on lines +166 to +169
def _run(query_object: QueryObject) -> pd.DataFrame:
table = cast(SqlaTable, query_object.datasource)
result = table.get_query_result(query_object)
return query_object.exec_post_processing(result.df)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: SqlaTable.get_query_result already performs time-offset processing and executes query_object.exec_post_processing before returning. Calling exec_post_processing again applies the pivot and flatten operations twice, so this regression test does not exercise the production result contract and can produce misleading results or fail when post-processing operations are not idempotent. [incomplete implementation]

Severity Level: Major ⚠️
- ❌ Regression assertions exercise a non-production pipeline.
- ⚠️ Non-idempotent post-processing can create false failures.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/unit_tests/common/test_time_column_offset_repro.py
**Line:** 166:169
**Comment:**
	*Incomplete Implementation: `SqlaTable.get_query_result` already performs time-offset processing and executes `query_object.exec_post_processing` before returning. Calling `exec_post_processing` again applies the pivot and flatten operations twice, so this regression test does not exercise the production result contract and can produce misleading results or fail when post-processing operations are not idempotent.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct — get_query_result already runs the offset join and exec_post_processing. Removed the second call; _run now returns get_query_result(...).df directly, so the test exercises the production result contract exactly once.

… comparison (SC-111332)

Overriding a chart's Time Column on a dashboard (via the native "Time
Column" filter or the Display Controls dropdown — both emit
`granularity_sqla` through `extra_form_data`) funnels through
`QueryContextFactory._apply_granularity`. That method re-points the BASE_AXIS
x-axis at the overridden column, but it also renamed the column's `label` to
the overridden column name.

The offset join in `processing_time_offsets`, the post-processing pivot
`index`, and the frontend (`getXAxisLabel`) all reference the x-axis by its
original saved label. Renaming the label desynchronized those consumers from
the label the saved chart still advertises. With a Time Comparison offset in
play, the result was keyed under a label nothing else recognized, collapsing
the series into a single data point.

Fix: for an adhoc (dict) x-axis, only swap the underlying `sqlExpression` to
the overridden column and keep the original `label`, so the join, the pivot
and the frontend keep matching. The bare-string x-axis path (which has no
distinct label) still replaces the column wholesale and realigns the pivot
`index`.

Adds a regression test that drives the full offset + pivot pipeline and
asserts the overridden Time Column plots across the range (multi-point)
under the original x-axis label instead of collapsing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@yousoph
yousoph force-pushed the sc-111332-time-column-single-point branch from f8ac93d to 76df474 Compare August 7, 2026 00:38
@bito-code-review

bito-code-review Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #4bf88a

Actionable Suggestions - 0
Review Details
  • Files reviewed - 3 · Commit Range: 76df474..76df474
    • superset/common/query_context_factory.py
    • tests/unit_tests/common/test_query_context_factory.py
    • tests/unit_tests/common/test_time_column_offset_repro.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@sadpandajoe sadpandajoe added the 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR label Aug 7, 2026
@github-actions github-actions Bot added 🎪 5272d59 🚦 building Environment 5272d59 status: building 🎪 5272d59 📅 2026-08-07T17-18 Environment 5272d59 created at 2026-08-07T17-18 🎪 5272d59 🤡 sadpandajoe Environment 5272d59 requested by sadpandajoe 🎪 ⌛ 48h Environment expires after 48 hours (default) and removed 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR labels Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🎪 Showtime is building environment on GHA for 5272d59

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🎪 Showtime is building environment on GHA for 5272d59

@github-actions github-actions Bot added 🎪 5272d59 🚦 deploying Environment 5272d59 status: deploying 🎪 5272d59 🚦 failed Environment 5272d59 status: failed and removed 🎪 5272d59 🚦 building Environment 5272d59 status: building 🎪 5272d59 🚦 deploying Environment 5272d59 status: deploying labels Aug 7, 2026
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.37%. Comparing base (38ba4a6) to head (5272d59).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
superset/common/query_context_factory.py 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42875      +/-   ##
==========================================
+ Coverage   57.10%   66.37%   +9.27%     
==========================================
  Files        2857     2856       -1     
  Lines      161125   161106      -19     
  Branches    37060    37051       -9     
==========================================
+ Hits        92014   106941   +14927     
+ Misses      68259    52146   -16113     
- Partials      852     2019    +1167     
Flag Coverage Δ
hive 38.25% <0.00%> (+<0.01%) ⬆️
mysql 57.78% <0.00%> (?)
postgres 57.83% <0.00%> (?)
presto 40.20% <0.00%> (+<0.01%) ⬆️
python 59.23% <0.00%> (+18.96%) ⬆️
sqlite 57.45% <0.00%> (?)
unit 100.00% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added 🎪 5272d59 🚦 running Environment 5272d59 status: running 🎪 🎯 5272d59 Active environment pointer - 5272d59 is receiving traffic and removed 🎪 5272d59 🚦 failed Environment 5272d59 status: failed labels Aug 7, 2026
@github-actions github-actions Bot added 🎪 5272d59 🚦 running Environment 5272d59 status: running 🎪 5272d59 🌐 18.237.97.97:8080 Environment 5272d59 URL: http://18.237.97.97:8080 (click to visit) and removed 🎪 5272d59 🚦 running Environment 5272d59 status: running 🎪 🎯 5272d59 Active environment pointer - 5272d59 is receiving traffic labels Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🎪 Showtime deployed environment on GHA for 5272d59

Environment: http://18.237.97.97:8080 (admin/admin)
Lifetime: 48h auto-cleanup
Updates: New commits create fresh environments automatically

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

Labels

explore:time Related to the time filters in Explore size/L 🎪 ⌛ 48h Environment expires after 48 hours (default) 🎪 5272d59 🚦 running Environment 5272d59 status: running 🎪 5272d59 🤡 sadpandajoe Environment 5272d59 requested by sadpandajoe 🎪 5272d59 🌐 18.237.97.97:8080 Environment 5272d59 URL: http://18.237.97.97:8080 (click to visit) 🎪 5272d59 📅 2026-08-07T17-18 Environment 5272d59 created at 2026-08-07T17-18

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants