Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs-mintlify/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@
"group": "DAX API",
"root": "reference/core-data-apis/dax-api/index",
"pages": [
"reference/core-data-apis/dax-api/cross-view-filter",
"reference/core-data-apis/dax-api/reference"
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: Cross-view filtering
description: "Enable cross-view filtering in the DAX API to build dashboards that span multiple views in Power BI."
---

By default, the [DAX API][ref-dax-api] exposes each [view][ref-views] as a
separate perspective in [Power BI][ref-powerbi]. Visualizations from different
views can't share the same filters, and a single report can't combine measures
and dimensions across views.

Cross-view filtering removes this limitation, letting you build dashboards
that span multiple views and apply filters consistently across visualizations.
It is supported in both [Live connection](#live-connection-mode) and
[DirectQuery](#directquery-mode) modes.

To use cross-view filters such as slicers, you must enable single-perspective mode
in the DAX API. To enable single-perspective mode in the DAX API,
set the `CUBEJS_DAX_SINGLE_PERSPECTIVE` environment variable to `true`.

In single-perspective mode, all views are exposed as part of a single perspective.
This allows you to use a single connection and show visualizations from different
views on the same dashboard.

<Warning>

Single-perspective mode changes the way views are exposed by the DAX API.
Because of that, it is not backwards-compatible with dashboards created before
enabling this environment variable.

</Warning>

## Live connection mode

In Live connection mode, cross-view filters work automatically, provided that
the following two conditions are met:

- The column used in the filter must be present in every view that the
cross-filter has to be applied to, and named exactly the same in each view.
- The column in all such views must resolve to the same cube member.

For example, if views `orders_view` and `users_view` both expose a column named
`address_country` pointing to the `country.country` cube member, cross-filtering
will be applied across these views.

If the `address_country` column in `orders_view` points to `orders.country`
while the `address_country` column in `users_view` points to `users.country`,
cross-filtering will not be applied, because the columns resolve to different
cube members.

Likewise, if `orders_view` exposes an `address_country` column pointing to
`country.country` and `users_view` exposes a `country` column also pointing to
`country.country`, cross-filtering will not be applied, because the column
names differ between the views.

## DirectQuery mode

In DirectQuery mode, cross-view filters require the same two conditions as in
[Live connection mode](#live-connection-mode): the filter column must be
present in every view with the exact same name, and it must resolve to the
same cube member across those views.

In addition, you must manually configure relationships between tables in Power
BI so that filters propagate across views:

- Open **Model view** in the left sidebar.
- On the right, open the **Data** sidebar, then the **Model** tab.
- Right-click **Relationships** and choose **New relationship**.
- In the **Properties** sidebar, select a table and a column.
- For cardinality, choose **Many to many (\*:\*)**. Ignore the warning.
- Select another table with the same column.
- For cross-filter direction, select **Both**.
- Click **Apply changes**.

Once the relationship is configured correctly, cross-view filters will work in
DirectQuery mode.

The steps above describe configuring a relationship between two tables. When
many views need to be cross-filtered, the best practice is to create a single
view in the Cube data model that includes all the columns from the views that
need to be cross-filtered, and then create relationships between this large
view and each of the other views. This keeps the relationship graph in Power
BI simple and avoids configuring pairwise relationships between every
combination of views.


[ref-dax-api]: /reference/core-data-apis/dax-api
[ref-views]: /docs/data-modeling/views
[ref-powerbi]: /admin/connect-to-data/visualization-tools/powerbi
12 changes: 12 additions & 0 deletions docs-mintlify/reference/core-data-apis/dax-api/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,20 @@ The DAX API only exposes [views][ref-views], not cubes.

</Info>

## Cross-view filtering

By default, each view is exposed as a separate perspective in Power BI, so
visualizations from different views can't share the same filters. Cross-view
filtering lets you build dashboards that span multiple views and apply filters
consistently across visualizations, in both Live connection and DirectQuery
modes.

See [Cross-view filtering][ref-cross-view-filter] for details on how to enable
and use it.


[ref-powerbi]: /admin/connect-to-data/visualization-tools/powerbi
[ref-cross-view-filter]: /reference/core-data-apis/dax-api/cross-view-filter
[link-dax]: https://learn.microsoft.com/en-us/dax/
[ref-sql-api]: /reference/sql-api
[ref-ref-dax-api]: /reference/dax-api/reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ use cubenativeutils::CubeError;
use std::any::Any;
use std::rc::Rc;

/// Dialect-independent callbacks to the JavaScript side, used
/// during compilation and planning: SQL templates, time-series
/// generation, allocated params, pre-aggregation lookup, join-tree
/// resolution. Dialect-specific helpers live behind `DriverTools`,
/// reachable via `driver_tools()`.
#[nativebridge::native_bridge]
pub trait BaseTools {
fn driver_tools(&self, external: bool) -> Result<Rc<dyn DriverTools>, CubeError>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ use cubenativeutils::CubeError;
use std::any::Any;
use std::rc::Rc;

/// Dialect-specific SQL helpers implemented by the JS driver. Used
/// only at SQL-generation time, once the target dialect is known:
/// timezone conversion, granularity-grouped columns, HLL functions,
/// interval arithmetic, type casts.
#[nativebridge::native_bridge]
pub trait DriverTools {
fn convert_tz(&self, field: String) -> Result<String, CubeError>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use cubenativeutils::CubeError;
use std::rc::Rc;
use std::{any::Any, cell::RefCell, rc::Weak};

/// Result of evaluating a member's `sql` JS function: a single SQL
/// string, or — for pre-aggregation `dimensions:` / `measures:`
/// reference lists — one string per referenced member.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SqlTemplate {
String(String),
Expand All @@ -40,6 +43,9 @@ impl<IT: InnerTypes> NativeDeserialize<IT> for SqlTemplate {
}
}

/// Column argument passed to
/// `FILTER_PARAMS.cube.member.filter(...)`: either a plain column
/// name string, or a JS callback that produces the SQL snippet.
#[derive(Clone)]
pub enum FilterParamsColumn {
String(String),
Expand Down Expand Up @@ -179,6 +185,9 @@ pub struct SecutityContextProps {
pub values: Vec<String>,
}

/// Dependencies collected while compiling a member `sql` function.
/// Each `{arg:N}` / `{fp:N}` / `{fg:N}` / `{sv:N}` placeholder in
/// the produced `SqlTemplate` indexes into one of these vectors.
#[derive(Default, Clone, Debug)]
pub struct SqlTemplateArgs {
pub symbol_paths: Vec<Vec<String>>,
Expand Down Expand Up @@ -296,6 +305,11 @@ impl ProxyStateWeak {
}
}

/// A member's `sql:` function as provided by the JS schema compiler.
/// `compile_template_sql` invokes the function under proxied
/// arguments (`{CUBE}`, `FILTER_PARAMS`, `FILTER_GROUP`,
/// `SECURITY_CONTEXT`, `SQL_UTILS`) and returns the resulting SQL
/// template together with the dependencies the function touched.
pub trait MemberSql {
fn args_names(&self) -> &Vec<String>;
fn as_any(self: Rc<Self>) -> Rc<dyn Any>;
Expand All @@ -306,6 +320,12 @@ pub trait MemberSql {
) -> Result<(SqlTemplate, SqlTemplateArgs), CubeError>;
}

/// Neon-backed implementation of `MemberSql`. `compile_template_sql`
/// calls the JS function with proxy objects that record every
/// accessed member path, `FILTER_PARAMS` / `FILTER_GROUP` call, and
/// `SECURITY_CONTEXT.x.filter(...)` / `unsafeValue()` reference into
/// a shared state, then returns the produced template together with
/// that state as `SqlTemplateArgs`.
pub struct NativeMemberSql<IT: InnerTypes> {
native_object: NativeObjectHandle<IT>,
args_names: Vec<String>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
//! DTO layer for talking to the JavaScript side of Cube.
//!
//! Every type here mirrors the shape of an object delivered by the
//! schema compiler — cube and member definitions, filter params,
//! callbacks, security context, and so on. Tesseract reads these
//! types as input; no business logic lives here.

pub mod base_query_options;
pub mod base_tools;
pub mod case_definition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ use cubenativeutils::CubeError;
use std::any::Any;
use std::rc::Rc;

/// Type-erased reference to the JS-side security context object.
/// Used to materialise `SECURITY_CONTEXT.x.filter(...)` /
/// `unsafeValue()` proxies when compiling a member SQL function.
#[nativebridge::native_bridge]
pub trait SecurityContext {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ use minijinja::{value::Value, Environment};
use std::collections::HashMap;
use std::marker::PhantomData;

/// Jinja2 template rendering for SQL templates. The native
/// implementation keeps the parsed templates locally and renders
/// without crossing the JS boundary.
pub trait SqlTemplatesRender {
fn contains_template(&self, template_name: &str) -> bool;
fn render_template(&self, name: &str, ctx: Value) -> Result<String, CubeError>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ use cubenativeutils::CubeError;
use std::any::Any;
use std::rc::Rc;

/// Opaque holder for the JS-side `SQL_UTILS` object. Tesseract only
/// forwards it back into member `sql` functions; the methods
/// invoked on it are a JS concern.
#[nativebridge::native_bridge]
pub trait SqlUtils {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ use std::rc::Rc;

logical_source_enum!(AggregateMultipliedSubquerySource, [Cube, MeasureSubquery]);

/// Subquery that aggregates a multiplied measure: a `keys_subquery`
/// produces the unique key set, a `source` (cube or
/// `MeasureSubquery`) supplies the values, optional
/// `dimension_subqueries` materialise sub-query dimensions, and
/// `pre_aggregation_override` lets a matched pre-aggregation
/// short-circuit the whole CTE.
pub struct AggregateMultipliedSubquery {
pub schema: Rc<LogicalSchema>,
pub keys_subquery: Rc<KeysSubQuery>,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use cubenativeutils::CubeError;
use std::rc::Rc;
use typed_builder::TypedBuilder;

/// Marker for an "original SQL" pre-aggregation attached to a cube
/// — the physical builder uses its name to substitute the cube's
/// table expression with the matching pre-aggregation source.
#[derive(Clone, TypedBuilder)]
pub struct OriginalSqlPreAggregation {
name: String,
Expand All @@ -24,6 +27,9 @@ impl PrettyPrint for OriginalSqlPreAggregation {
}
}

/// A cube referenced from the logical plan — wraps the planner's
/// `BaseCube` and optionally pins a matching "original SQL"
/// pre-aggregation as the cube's source.
#[derive(Clone, TypedBuilder)]
pub struct Cube {
cube: Rc<BaseCube>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use crate::planner::MemberSymbol;
use cubenativeutils::CubeError;
use std::rc::Rc;

/// Materialisation of a `sub_query: true` dimension: a subquery
/// that groups by the owning cube's primary keys and computes the
/// dimension's measure expression, then is joined back to the host
/// query on those keys.
pub struct DimensionSubQuery {
pub query: Rc<Query>,
pub primary_keys_dimensions: Vec<Rc<MemberSymbol>>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use super::pretty_print::*;
use crate::planner::filter::{Filter, FilterItem};
use itertools::Itertools;

/// All filters of a query split by what they target. WHERE-style
/// filters (`time_dimensions_filters`, `dimensions_filters`,
/// `segments`) are surfaced together via `all_filters`; HAVING-style
/// `measures_filter` stays separate.
#[derive(Default)]
pub struct LogicalFilter {
pub dimensions_filters: Vec<FilterItem>,
Expand Down
Loading
Loading