Skip to content

AdHoc filters apply to all views, breaking multi-view dashboards #307

@samjewell

Description

@samjewell

Problem

The current implementation injects every dashboard-level AdHoc filter into every Cube query, regardless of which view that query targets (normalizeCubeQuery.ts, getAdHocFiltersvalidFilters).

Because Cube views are sealed namespaces — every member is fully-qualified by its view (view_a.region, never just region) — applying a filter on view_a.region to a query on view_b results in Cube's /v1/load returning an error on the unknown member.

In practice, any AdHoc filter on a dashboard with panels from more than one view turns the unrelated panels red. Combined with the deduped getTagKeys list (which surfaces region once even if view_a.region and view_b.region are semantically distinct columns), this is both a correctness bug and a UX trap.

Reproduction

  1. Dashboard with two panels: panel A queries view_a (which has view_a.region); panel B queries view_b (no region).
  2. Add an AdHoc filter region = uk (the dropdown shows a single deduped region entry).
  3. Panel A filters correctly. Panel B errors with an unknown-member response from Cube.

Why this is surprisingly subtle

When a user adds region = uk to a multi-panel dashboard, what do they actually mean?

  • Strict-Cube semantics — "filter view_a.region, the literal column": other views aren't filtered, by design.
  • Business semantics — "limit the dashboard to UK data, wherever that is represented": filter is conceptually cross-view.

We can't fully deliver business semantics without explicit cross-view equivalence (a Looker LookML "explore"-style construct) declared in the model. So we have to either ship the strict-Cube behaviour and signal it well, or surface the ambiguity in the UI before the filter is created.

Options considered

Option 1 — Drop inapplicable filters at injection time (ship now)

In normalizeCubeQuery, infer the query's view from dimensions ∪ measures ∪ panel-filters (same logic as getViewSelectionState) and drop any AdHoc filter whose member belongs to a different view. Surface dropped filters in the SQL preview ("Skipped N inapplicable AdHoc filters: …").

Pros: small change (~tens of LOC), eliminates the red-panels failure mode immediately.
Cons: silent at the dashboard level — users still type `region = uk` expecting global effect. Per-panel hint is the wrong place to surface a dashboard-wide expectation gap.

Sketch:

  1. Build `Map<member, view>` from `metadata.dimensions/measures` (already has `Cube` field on the backend response).
  2. In `normalizeCubeQuery`, filter `adHocFilters` to those whose `key`'s view matches the query's inferred view. If no view can be inferred (no dims/measures/filters yet), skip injection entirely.
  3. Return the dropped list from `normalizeCubeQuery` so `useCubeQueryJson` / `SQLPreview` can show a "skipped N" hint.
  4. Optionally prefix `getTagKeys` `text` with the view name to disambiguate `view_a.region` vs `view_b.region` in the filter bar (overlaps with Feature: Looker-style intelligent display names (view + member, context-aware) and query builder view grouping #107).

Option 2 — One AdHoc filter set per view (medium-term)

Make AdHoc filters explicitly view-scoped at the dashboard schema layer: one `AdHocFiltersVariable` per view, each with its own keys/values fetched from that view only. Click-to-filter from a panel writes to its view's variable.

Pros: kills the semantic mismatch at the root — the user explicitly knows which view they're filtering. Click-to-filter no longer pollutes unrelated panels. Dropdown values are cleanly scoped (no cross-view dimensional collision). Composes with — rather than replacing — Option 1 / getFiltersApplicability.
Cons: Grafana's `AdHocFiltersVariable` keys off `{ datasource: { uid } }` (scenes docs) and doesn't currently model "multiple instances on the same datasource UID with a scope qualifier." Implementing this needs either:

  • (a) one Grafana datasource per view — bad UX for setup;
  • (b) a custom Scene-level filter UI shipped with the plugin; or
  • (c) an upstream change to support a `scope`/`group` qualifier on AdHoc variables.

Option 3 — Restrict each dashboard to a single view

Rejected. Violates Grafana's composable-dashboard ethos. Pushes the burden onto data modelers, who'll respond by building giant catch-all super-views. Doesn't help the cross-dashboard filter-carry case in grafana/grafana#108011.

Option 4 (upstream) — Implement `getFiltersApplicability` (long-term)

Grafana is adding `getFiltersApplicability` on the datasource interface: the datasource returns which filters apply to which queries, and Grafana's UI greys out the inapplicable ones with a visual cue (instead of "applying everything and getting No Data"). Maps perfectly onto views: a filter is applicable iff its member's view matches the query's view.

Pros: correct runtime behaviour, visual UX feedback, works across dashboard-link filter inheritance for free.
Cons: depends on upstream timelines.

Recommendation

  1. Ship Option 1 immediately — eliminates the red-panels failure mode, small surface area.
  2. File a follow-up in grafana/grafana (or grafana/scenes) for upstream AdHocFiltersVariable support for view-scoped filter sets (Option 2), and link / track `getFiltersApplicability` (Option 4).
  3. Adopt getFiltersApplicability when it lands.
  4. Re-evaluate Option 2 once the upstream primitive exists; it remains the best answer to the semantic-mismatch question even after Option 4 ships, because Option 4 is a runtime safety net while Option 2 is a schema-level expression of intent.

Caveat to communicate in docs

Option 1 changes the click-to-filter behaviour the README advertises: clicking customer = BBC on a view_a panel will only filter view_a panels — view_b panels stay unfiltered. That's the correct behaviour given Cube's semantics, but it's a documentation/expectations change.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    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