From 4fbb9c3e50eb262c8b6cb3e780a967b838b4670c Mon Sep 17 00:00:00 2001
From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com>
Date: Fri, 5 Jun 2026 13:44:25 +0000
Subject: [PATCH 1/3] docs: document multi-stage filter directive (mode,
exclude, keep_only, include)
---
docs-mintlify/docs/data-modeling/measures.mdx | 193 ++++++++++++++++
.../reference/data-modeling/dimensions.mdx | 70 +++++-
.../reference/data-modeling/measures.mdx | 213 ++++++++++++++++++
3 files changed, 475 insertions(+), 1 deletion(-)
diff --git a/docs-mintlify/docs/data-modeling/measures.mdx b/docs-mintlify/docs/data-modeling/measures.mdx
index 0a443613af3bb..d50d461aeaffd 100644
--- a/docs-mintlify/docs/data-modeling/measures.mdx
+++ b/docs-mintlify/docs/data-modeling/measures.mdx
@@ -368,6 +368,197 @@ measures:
type: rank
```
+### Filter directives
+
+By default, a multi-stage measure inherits the query's filters into its inner
+aggregation stage. The [`filter`][ref-filter-directives] parameter lets you
+override this — dropping, replacing, or augmenting filters before the inner
+stage runs.
+
+This is the building block for "share of total" against an unfiltered base,
+measures that ignore certain filters (e.g., always-completed revenue), and
+measures that pin themselves to a fixed slice regardless of the surrounding
+query.
+
+The `filter` parameter accepts four sub-parameters: `mode` (`relative` —
+the default — or `fixed`), `exclude`, `keep_only`, and `include`.
+
+**Drop a specific query filter.** The measure ignores any filter the query
+places on `status`, but still respects every other filter:
+
+
+
+```yaml title="YAML"
+measures:
+ - name: revenue_ignoring_status
+ multi_stage: true
+ sql: "{revenue}"
+ type: number
+ filter:
+ exclude:
+ - status
+```
+
+```javascript title="JavaScript"
+measures: {
+ revenue_ignoring_status: {
+ multi_stage: true,
+ sql: `${revenue}`,
+ type: `number`,
+ filter: {
+ exclude: () => [status]
+ }
+ }
+}
+```
+
+
+
+**Replace a query filter.** Strip the inherited filter on `status` and force
+the inner stage to filter on `status = 'cancelled'` instead:
+
+
+
+```yaml title="YAML"
+measures:
+ - name: revenue_cancelled
+ multi_stage: true
+ sql: "{revenue}"
+ type: sum
+ filter:
+ exclude:
+ - status
+ include:
+ - member: status
+ operator: equals
+ values:
+ - cancelled
+```
+
+```javascript title="JavaScript"
+measures: {
+ revenue_cancelled: {
+ multi_stage: true,
+ sql: `${revenue}`,
+ type: `sum`,
+ filter: {
+ exclude: () => [status],
+ include: [
+ {
+ member: `status`,
+ operator: `equals`,
+ values: [`cancelled`]
+ }
+ ]
+ }
+ }
+}
+```
+
+
+
+**Always compute against a fixed slice.** With `mode: fixed`, all inherited
+filters are discarded and only the entries in `include` apply, even when the
+measure is referenced from another multi-stage measure:
+
+
+
+```yaml title="YAML"
+measures:
+ - name: revenue_completed_fixed
+ multi_stage: true
+ sql: "{revenue}"
+ type: sum
+ filter:
+ mode: fixed
+ include:
+ - member: status
+ operator: equals
+ values:
+ - completed
+```
+
+```javascript title="JavaScript"
+measures: {
+ revenue_completed_fixed: {
+ multi_stage: true,
+ sql: `${revenue}`,
+ type: `sum`,
+ filter: {
+ mode: `fixed`,
+ include: [
+ {
+ member: `status`,
+ operator: `equals`,
+ values: [`completed`]
+ }
+ ]
+ }
+ }
+}
+```
+
+
+
+**Boolean groups.** `include` clauses can be combined with `or` / `and`
+groups to express compound filters:
+
+
+
+```yaml title="YAML"
+measures:
+ - name: revenue_top_cities
+ multi_stage: true
+ sql: "{revenue}"
+ type: sum
+ filter:
+ include:
+ - or:
+ - member: city
+ operator: equals
+ values:
+ - NYC
+ - member: city
+ operator: equals
+ values:
+ - SF
+```
+
+```javascript title="JavaScript"
+measures: {
+ revenue_top_cities: {
+ multi_stage: true,
+ sql: `${revenue}`,
+ type: `sum`,
+ filter: {
+ include: [
+ {
+ or: [
+ {
+ member: `city`,
+ operator: `equals`,
+ values: [`NYC`]
+ },
+ {
+ member: `city`,
+ operator: `equals`,
+ values: [`SF`]
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
+```
+
+
+
+`filter` can also be combined with [`keep_only`][ref-filter-directives] — the
+inverse of `exclude` — to forward only filters on a chosen set of members. The
+same `filter` parameter is available on
+[multi-stage dimensions][ref-dim-filter].
+
### Conditional measures
Conditional measures depend on the value of a dimension, using the
@@ -426,6 +617,8 @@ measures:
[ref-group-by]: /reference/data-modeling/measures#group_by
[ref-reduce-by]: /reference/data-modeling/measures#reduce_by
[ref-add-group-by]: /reference/data-modeling/measures#add_group_by
+[ref-filter-directives]: /reference/data-modeling/measures#filter
+[ref-dim-filter]: /reference/data-modeling/dimensions#filter
[ref-case]: /reference/data-modeling/measures#case
[ref-switch-dim]: /reference/data-modeling/dimensions#type
[ref-tesseract-env]: /reference/configuration/environment-variables#cubejs_tesseract_sql_planner
diff --git a/docs-mintlify/reference/data-modeling/dimensions.mdx b/docs-mintlify/reference/data-modeling/dimensions.mdx
index a08353ef48b95..caa4fdcc73e8a 100644
--- a/docs-mintlify/reference/data-modeling/dimensions.mdx
+++ b/docs-mintlify/reference/data-modeling/dimensions.mdx
@@ -1302,6 +1302,71 @@ cube(`fiscal_calendar`, {
+### `filter`
+
+The `filter` parameter is used with [multi-stage dimensions][ref-multi-stage] to
+control which query-time filters reach the inner aggregation stage. It accepts
+the same shape as the [`filter` parameter on measures][ref-measure-filter] —
+`mode` (`relative` or `fixed`), `exclude`, `keep_only`, and `include`.
+
+
+
+The `filter` parameter on multi-stage measures and dimensions is powered by
+Tesseract, the [next-generation data modeling engine][link-tesseract]. Tesseract
+is currently in preview. Use the
+[`CUBEJS_TESSERACT_SQL_PLANNER`][ref-tesseract-env] environment variable to
+enable it.
+
+
+
+
+
+```yaml title="YAML"
+dimensions:
+ - name: status_normalized
+ type: string
+ sql: "{CUBE.status}"
+ multi_stage: true
+ add_group_by:
+ - status
+ filter:
+ mode: relative
+ keep_only:
+ - city
+ include:
+ - member: status
+ operator: notEquals
+ values:
+ - archived
+```
+
+```javascript title="JavaScript"
+dimensions: {
+ status_normalized: {
+ type: `string`,
+ sql: `${CUBE.status}`,
+ multi_stage: true,
+ add_group_by: [status],
+ filter: {
+ mode: `relative`,
+ keep_only: () => [city],
+ include: [
+ {
+ member: `status`,
+ operator: `notEquals`,
+ values: [`archived`]
+ }
+ ]
+ }
+ }
+}
+```
+
+
+
+See the [measures reference][ref-measure-filter] for the full description of
+each sub-parameter.
+
[ref-ai-context]: /docs/data-modeling/ai-context
[ref-ref-cubes]: /reference/data-modeling/cube
[ref-schema-ref-joins]: /reference/data-modeling/joins
@@ -1334,4 +1399,7 @@ cube(`fiscal_calendar`, {
[ref-references]: /docs/data-modeling/syntax#references
[ref-filter-params]: /reference/data-modeling/context-variables#filter_params
[link-encode-uri-component]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
-[ref-rest-filters]: /reference/rest-api/query-format#query-properties
\ No newline at end of file
+[ref-rest-filters]: /reference/rest-api/query-format#query-properties
+[ref-multi-stage]: /docs/data-modeling/measures#multi-stage-measures
+[ref-measure-filter]: /reference/data-modeling/measures#filter
+[ref-tesseract-env]: /reference/configuration/environment-variables#cubejs_tesseract_sql_planner
\ No newline at end of file
diff --git a/docs-mintlify/reference/data-modeling/measures.mdx b/docs-mintlify/reference/data-modeling/measures.mdx
index 8c78673af880b..16e791c25672f 100644
--- a/docs-mintlify/reference/data-modeling/measures.mdx
+++ b/docs-mintlify/reference/data-modeling/measures.mdx
@@ -766,6 +766,217 @@ dimensions will be included in the inner stage's `GROUP BY` but will *not* appea
in the outer aggregation — they are used only to define the granularity at which
the base measure is computed before the outer aggregation is applied.
+### `filter`
+
+The `filter` parameter is used with [multi-stage measures][ref-multi-stage] to
+control which query-time filters reach the inner aggregation stage. It allows
+you to drop, replace, or augment filters that would otherwise be inherited from
+the query.
+
+This is commonly used when a multi-stage calculation needs to ignore filters on
+specific dimensions (for example, computing a "share of total" against an
+unfiltered base) or apply its own additional filters on top of the query's
+filters.
+
+
+
+The `filter` parameter on multi-stage measures and dimensions is powered by
+Tesseract, the [next-generation data modeling engine][link-tesseract]. Tesseract
+is currently in preview. Use the
+[`CUBEJS_TESSERACT_SQL_PLANNER`][ref-tesseract-env] environment variable to
+enable it.
+
+
+
+
+
+```yaml title="YAML"
+measures:
+ - name: revenue
+ sql: amount
+ type: sum
+
+ - name: revenue_completed_only
+ multi_stage: true
+ sql: "{revenue}"
+ type: number
+ filter:
+ mode: relative
+ exclude:
+ - status
+ include:
+ - member: status
+ operator: equals
+ values:
+ - completed
+ - or:
+ - member: city
+ operator: equals
+ values:
+ - NYC
+ - member: city
+ operator: equals
+ values:
+ - SF
+```
+
+```javascript title="JavaScript"
+measures: {
+ revenue: {
+ sql: `amount`,
+ type: `sum`
+ },
+
+ revenue_completed_only: {
+ multi_stage: true,
+ sql: `${revenue}`,
+ type: `number`,
+ filter: {
+ mode: `relative`,
+ exclude: () => [status],
+ include: [
+ {
+ member: `status`,
+ operator: `equals`,
+ values: [`completed`]
+ },
+ {
+ or: [
+ {
+ member: `city`,
+ operator: `equals`,
+ values: [`NYC`]
+ },
+ {
+ member: `city`,
+ operator: `equals`,
+ values: [`SF`]
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
+```
+
+
+
+The `filter` parameter accepts an object with the following keys, all of which
+are optional. At least one of `exclude`, `keep_only`, or `include` must be
+provided.
+
+#### `mode`
+
+Controls how the inner stage's filter set is composed. Can be either of:
+
+| Mode | Description |
+|---|---|
+| `relative` (default) | Start from the filters that would otherwise apply to the inner stage (the query's filters minus anything that was already consumed by an outer multi-stage wrapper), then apply `exclude` / `keep_only` / `include` on top. |
+| `fixed` | Ignore the inherited filter state entirely. The inner stage starts from an empty set of filters and only the entries listed in `include` are applied. |
+
+Use `fixed` when a multi-stage measure must always be computed against the same
+slice of data regardless of which other multi-stage measures wrap it. Use
+`relative` (the default) when the calculation should still react to the
+surrounding query.
+
+#### `exclude`
+
+A list of dimension or segment names. Any query-time filter on a listed member
+is dropped before the inner stage runs. This is useful when a calculation must
+be insensitive to a specific dimension's filter (e.g., a denominator that
+should not be filtered by `status` even when the query filters by it).
+
+In JavaScript, `exclude` is written as a function returning an array of
+dimension references — the same pattern as
+[`group_by`](#group_by) and [`reduce_by`](#reduce_by):
+
+
+
+```yaml title="YAML"
+filter:
+ exclude:
+ - status
+ - completed_orders # may also reference a segment
+```
+
+```javascript title="JavaScript"
+filter: {
+ exclude: () => [status, completed_orders]
+}
+```
+
+
+
+#### `keep_only`
+
+The inverse of `exclude`. A list of dimension or segment names — only filters
+on the listed members are forwarded to the inner stage; everything else is
+dropped.
+
+
+
+```yaml title="YAML"
+filter:
+ keep_only:
+ - city
+```
+
+```javascript title="JavaScript"
+filter: {
+ keep_only: () => [city]
+}
+```
+
+
+
+#### `include`
+
+A list of filter clauses that are added on top of the resulting filter set.
+Each clause is either a single-member filter or a boolean group:
+
+- A **member filter** has the shape `{ member, operator, values }`, where
+ `operator` matches the operators available in the
+ [REST API query format][ref-rest-query-ops] (`equals`, `notEquals`,
+ `contains`, `notContains`, `startsWith`, `notStartsWith`, `endsWith`,
+ `notEndsWith`, `in`, `notIn`, `gt`, `gte`, `lt`, `lte`, `set`, `notSet`,
+ `inDateRange`, `notInDateRange`, `onTheDate`, `beforeDate`, `beforeOrOnDate`,
+ `afterDate`, `afterOrOnDate`, `measureFilter`). `values` is required for all
+ operators except `set` and `notSet`.
+- A **boolean group** has the shape `{ or: [ ... ] }` or `{ and: [ ... ] }`,
+ containing nested member filters or further boolean groups.
+
+`exclude` and `include` can be combined to *replace* a filter — `exclude` first
+strips the inherited filter on a member, then `include` adds the desired one.
+
+
+
+```yaml title="YAML"
+filter:
+ exclude:
+ - status
+ include:
+ - member: status
+ operator: equals
+ values:
+ - cancelled
+```
+
+```javascript title="JavaScript"
+filter: {
+ exclude: () => [status],
+ include: [
+ {
+ member: `status`,
+ operator: `equals`,
+ values: [`cancelled`]
+ }
+ ]
+}
+```
+
+
+
### `time_shift`
The `time_shift` parameter is used to configure a [time shift][ref-time-shift] for a
@@ -1431,6 +1642,8 @@ cube(`orders`, {
[ref-apis]: /reference
[ref-rolling-window]: /docs/data-modeling/measures#rolling-windows
[link-tesseract]: https://cube.dev/blog/introducing-next-generation-data-modeling-engine
+[ref-tesseract-env]: /reference/configuration/environment-variables#cubejs_tesseract_sql_planner
+[ref-rest-query-ops]: /reference/core-data-apis/rest-api/query-format#filters-operators
[ref-multi-stage]: /docs/data-modeling/measures#multi-stage-measures
[ref-time-shift]: /docs/data-modeling/measures#time-shift
[ref-nested-aggregate]: /docs/data-modeling/measures#nested-aggregates
From 88d1258cc6ef8f249952ded7f7b81e706c47a0af Mon Sep 17 00:00:00 2001
From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com>
Date: Fri, 5 Jun 2026 14:07:02 +0000
Subject: [PATCH 2/3] docs: add recipe for filtering multi-stage measures
---
docs-mintlify/docs.json | 1 +
docs-mintlify/docs/data-modeling/measures.mdx | 6 +
.../filtering-multi-stage-measures.mdx | 526 ++++++++++++++++++
3 files changed, 533 insertions(+)
create mode 100644 docs-mintlify/recipes/data-modeling/filtering-multi-stage-measures.mdx
diff --git a/docs-mintlify/docs.json b/docs-mintlify/docs.json
index 0057b8293cc03..991dd0b2dbd19 100644
--- a/docs-mintlify/docs.json
+++ b/docs-mintlify/docs.json
@@ -608,6 +608,7 @@
"recipes/data-modeling/filtered-aggregates",
"recipes/data-modeling/share-of-total",
"recipes/data-modeling/period-over-period",
+ "recipes/data-modeling/filtering-multi-stage-measures",
"recipes/data-modeling/passing-dynamic-parameters-in-a-query",
"recipes/data-modeling/using-dynamic-measures",
"recipes/data-modeling/dynamic-union-tables",
diff --git a/docs-mintlify/docs/data-modeling/measures.mdx b/docs-mintlify/docs/data-modeling/measures.mdx
index d50d461aeaffd..5aa96673c6021 100644
--- a/docs-mintlify/docs/data-modeling/measures.mdx
+++ b/docs-mintlify/docs/data-modeling/measures.mdx
@@ -559,6 +559,11 @@ inverse of `exclude` — to forward only filters on a chosen set of members. The
same `filter` parameter is available on
[multi-stage dimensions][ref-dim-filter].
+For end-to-end patterns — always-on baselines, replacing a filter, pinning a
+baseline through chained calculations, segment-aware aggregates, and compound
+boolean filters — see the
+[Filtering multi-stage measures recipe][ref-filter-recipe].
+
### Conditional measures
Conditional measures depend on the value of a dimension, using the
@@ -619,6 +624,7 @@ measures:
[ref-add-group-by]: /reference/data-modeling/measures#add_group_by
[ref-filter-directives]: /reference/data-modeling/measures#filter
[ref-dim-filter]: /reference/data-modeling/dimensions#filter
+[ref-filter-recipe]: /recipes/data-modeling/filtering-multi-stage-measures
[ref-case]: /reference/data-modeling/measures#case
[ref-switch-dim]: /reference/data-modeling/dimensions#type
[ref-tesseract-env]: /reference/configuration/environment-variables#cubejs_tesseract_sql_planner
diff --git a/docs-mintlify/recipes/data-modeling/filtering-multi-stage-measures.mdx b/docs-mintlify/recipes/data-modeling/filtering-multi-stage-measures.mdx
new file mode 100644
index 0000000000000..a7847d6be2046
--- /dev/null
+++ b/docs-mintlify/recipes/data-modeling/filtering-multi-stage-measures.mdx
@@ -0,0 +1,526 @@
+---
+title: Filtering multi-stage measures
+description: Use filter directives to drop, replace, or pin filters on multi-stage measures and dimensions — for always-on baselines, fixed cohorts, segment-aware aggregates, and chained calculations.
+---
+
+## Use case
+
+By default, a multi-stage measure inherits all query-time filters into its
+inner aggregation stage. The [`filter`][ref-filter-directives] parameter
+overrides that behavior — you can drop specific filters, replace them, or pin
+the measure to a fixed slice regardless of what the surrounding query asks
+for.
+
+This is the right tool when:
+
+- A baseline measure should ignore a filter the query applies (e.g.,
+ always-completed revenue, even when the query filters by `status`).
+- A comparison measure should be computed against a different cohort than
+ the query (actual vs. last-year, region X vs. region Y).
+- A measure must remain pinned to a fixed slice even when it is referenced
+ from another multi-stage measure higher in the chain.
+- You want segment-aware aggregates that respect or ignore segment filters
+ selectively.
+
+
+
+The `filter` parameter on multi-stage measures and dimensions is powered by
+Tesseract, the [next-generation data modeling engine][link-tesseract].
+Tesseract is currently in preview. Use the
+[`CUBEJS_TESSERACT_SQL_PLANNER`][ref-tesseract-env] environment variable to
+enable it.
+
+
+
+## Data model
+
+The examples below extend a simple `orders` cube with a `total_amount`
+measure, a `completed_orders` segment, and a few dimensions:
+
+
+
+```yaml title="YAML"
+cubes:
+ - name: orders
+ sql_table: ECOMMERCE.ORDERS
+
+ dimensions:
+ - name: id
+ sql: id
+ type: number
+ primary_key: true
+
+ - name: status
+ sql: "{CUBE}.status"
+ type: string
+
+ - name: category
+ sql: "{CUBE}.category"
+ type: string
+
+ - name: city
+ sql: "{CUBE}.city"
+ type: string
+
+ - name: created_at
+ sql: "{CUBE}.created_at"
+ type: time
+
+ segments:
+ - name: completed_orders
+ sql: "{CUBE}.status = 'completed'"
+
+ measures:
+ - name: total_amount
+ sql: amount
+ type: sum
+ format: currency
+```
+
+```javascript title="JavaScript"
+cube(`orders`, {
+ sql_table: `ECOMMERCE.ORDERS`,
+
+ dimensions: {
+ id: {
+ sql: `id`,
+ type: `number`,
+ primary_key: true
+ },
+
+ status: {
+ sql: `${CUBE}.status`,
+ type: `string`
+ },
+
+ category: {
+ sql: `${CUBE}.category`,
+ type: `string`
+ },
+
+ city: {
+ sql: `${CUBE}.city`,
+ type: `string`
+ },
+
+ created_at: {
+ sql: `${CUBE}.created_at`,
+ type: `time`
+ }
+ },
+
+ segments: {
+ completed_orders: {
+ sql: `${CUBE}.status = 'completed'`
+ }
+ },
+
+ measures: {
+ total_amount: {
+ sql: `amount`,
+ type: `sum`,
+ format: `currency`
+ }
+ }
+})
+```
+
+
+
+Each section below adds one or more multi-stage measures on top of this base
+model.
+
+## Always-on baseline (drop a query filter)
+
+Goal: a baseline `total_amount_all_statuses` that always reflects revenue
+across every status, even when the query is filtered to a single status. Use
+`exclude` to drop the inherited filter:
+
+
+
+```yaml title="YAML"
+measures:
+ - name: total_amount_all_statuses
+ multi_stage: true
+ sql: "{total_amount}"
+ type: number
+ filter:
+ exclude:
+ - status
+```
+
+```javascript title="JavaScript"
+measures: {
+ total_amount_all_statuses: {
+ multi_stage: true,
+ sql: `${total_amount}`,
+ type: `number`,
+ filter: {
+ exclude: () => [status]
+ }
+ }
+}
+```
+
+
+
+A query that groups by `status` and filters to `status = 'pending'` will see
+`total_amount` constrained to pending orders, but `total_amount_all_statuses`
+will compute against every order — useful as a denominator for share-of-status
+calculations.
+
+## Replace a filter (actual vs. fixed cohort)
+
+Goal: alongside the query's filtered `total_amount`, show
+`total_amount_cancelled` — the cancelled-orders total — regardless of which
+`status` the query filters by. Combine `exclude` (to drop the query's
+`status` filter) with `include` (to apply your own):
+
+
+
+```yaml title="YAML"
+measures:
+ - name: total_amount_cancelled
+ multi_stage: true
+ sql: "{total_amount}"
+ type: sum
+ filter:
+ exclude:
+ - status
+ include:
+ - member: status
+ operator: equals
+ values:
+ - cancelled
+```
+
+```javascript title="JavaScript"
+measures: {
+ total_amount_cancelled: {
+ multi_stage: true,
+ sql: `${total_amount}`,
+ type: `sum`,
+ filter: {
+ exclude: () => [status],
+ include: [
+ {
+ member: `status`,
+ operator: `equals`,
+ values: [`cancelled`]
+ }
+ ]
+ }
+ }
+}
+```
+
+
+
+Without `exclude`, the query's `status = 'completed'` filter would be ANDed
+with `status = 'cancelled'`, producing an empty set. `exclude` strips the
+inherited filter first; `include` then adds the desired one.
+
+## Pin a baseline through a chained calculation
+
+`mode: relative` (the default) means that when a multi-stage measure is
+wrapped by another multi-stage measure, the inner one inherits the outer's
+filter state. `mode: fixed` opts out: the inner measure always starts from
+an empty filter set, applying only what's in its own `include`.
+
+This matters for chained calculations. In the model below, `t_completed_share`
+divides revenue for `category = 'books'` by total revenue for
+`category = 'books'`. The numerator wraps a `fixed` measure so its denominator
+stays pinned to books regardless of the surrounding query:
+
+
+
+```yaml title="YAML"
+measures:
+ - name: books_revenue_fixed
+ multi_stage: true
+ sql: "{total_amount}"
+ type: sum
+ filter:
+ mode: fixed
+ include:
+ - member: category
+ operator: equals
+ values:
+ - books
+
+ - name: books_completed_revenue
+ multi_stage: true
+ sql: "{books_revenue_fixed}"
+ type: number
+ filter:
+ include:
+ - member: status
+ operator: equals
+ values:
+ - completed
+
+ - name: books_completed_share
+ multi_stage: true
+ sql: "1.0 * {books_completed_revenue} / NULLIF({books_revenue_fixed}, 0)"
+ type: number
+ format: percent
+```
+
+```javascript title="JavaScript"
+measures: {
+ books_revenue_fixed: {
+ multi_stage: true,
+ sql: `${total_amount}`,
+ type: `sum`,
+ filter: {
+ mode: `fixed`,
+ include: [
+ {
+ member: `category`,
+ operator: `equals`,
+ values: [`books`]
+ }
+ ]
+ }
+ },
+
+ books_completed_revenue: {
+ multi_stage: true,
+ sql: `${books_revenue_fixed}`,
+ type: `number`,
+ filter: {
+ include: [
+ {
+ member: `status`,
+ operator: `equals`,
+ values: [`completed`]
+ }
+ ]
+ }
+ },
+
+ books_completed_share: {
+ multi_stage: true,
+ sql: `1.0 * ${books_completed_revenue} / NULLIF(${books_revenue_fixed}, 0)`,
+ type: `number`,
+ format: `percent`
+ }
+}
+```
+
+
+
+Because `books_revenue_fixed` uses `mode: fixed`, its inner stage ignores
+both the query's filters and the outer `status = 'completed'` filter applied
+by `books_completed_revenue`. If you switched it to `mode: relative`, the
+denominator would inherit `status = 'completed'` from the outer wrapper and
+the ratio would always be 1.
+
+## Filter on segments
+
+`exclude` and `keep_only` accept segment names too. Use this when a multi-stage
+measure should react to (or ignore) specific segment filters independently of
+the query's segment selection.
+
+Two complementary patterns:
+
+
+
+```yaml title="YAML"
+measures:
+ # Ignore any `completed_orders` segment the query applies.
+ - name: total_amount_ignoring_completion
+ multi_stage: true
+ sql: "{total_amount}"
+ type: number
+ filter:
+ exclude:
+ - completed_orders
+
+ # Only honor the `completed_orders` segment; drop everything else.
+ - name: total_amount_completed_only_segment
+ multi_stage: true
+ sql: "{total_amount}"
+ type: number
+ filter:
+ keep_only:
+ - completed_orders
+```
+
+```javascript title="JavaScript"
+measures: {
+ total_amount_ignoring_completion: {
+ multi_stage: true,
+ sql: `${total_amount}`,
+ type: `number`,
+ filter: {
+ exclude: () => [completed_orders]
+ }
+ },
+
+ total_amount_completed_only_segment: {
+ multi_stage: true,
+ sql: `${total_amount}`,
+ type: `number`,
+ filter: {
+ keep_only: () => [completed_orders]
+ }
+ }
+}
+```
+
+
+
+## Compound filters with boolean groups
+
+`include` entries can be combined with `or` / `and` groups for compound
+conditions. The example below counts revenue from large orders in the two
+top-tier cities — independent of the query's own filters:
+
+
+
+```yaml title="YAML"
+measures:
+ - name: top_city_large_orders_revenue
+ multi_stage: true
+ sql: "{total_amount}"
+ type: sum
+ filter:
+ mode: fixed
+ include:
+ - and:
+ - member: total_amount
+ operator: gt
+ values:
+ - "100"
+ - or:
+ - member: city
+ operator: equals
+ values:
+ - NYC
+ - member: city
+ operator: equals
+ values:
+ - SF
+```
+
+```javascript title="JavaScript"
+measures: {
+ top_city_large_orders_revenue: {
+ multi_stage: true,
+ sql: `${total_amount}`,
+ type: `sum`,
+ filter: {
+ mode: `fixed`,
+ include: [
+ {
+ and: [
+ {
+ member: `total_amount`,
+ operator: `gt`,
+ values: [`100`]
+ },
+ {
+ or: [
+ {
+ member: `city`,
+ operator: `equals`,
+ values: [`NYC`]
+ },
+ {
+ member: `city`,
+ operator: `equals`,
+ values: [`SF`]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
+```
+
+
+
+Boolean groups nest to any depth. Each leaf is a `{ member, operator, values }`
+clause and uses the same operators as the
+[REST API query format][ref-rest-query-ops].
+
+## Filter directives on multi-stage dimensions
+
+The `filter` parameter is also available on
+[multi-stage dimensions][ref-dim-filter]. The directive works the same way —
+it controls which filters reach the dimension's inner aggregation stage. A
+common pattern is normalizing a dimension across a fixed cohort:
+
+
+
+```yaml title="YAML"
+dimensions:
+ - name: status_excluding_archived
+ sql: "{CUBE.status}"
+ type: string
+ multi_stage: true
+ add_group_by:
+ - status
+ filter:
+ mode: relative
+ include:
+ - member: status
+ operator: notEquals
+ values:
+ - archived
+```
+
+```javascript title="JavaScript"
+dimensions: {
+ status_excluding_archived: {
+ sql: `${CUBE.status}`,
+ type: `string`,
+ multi_stage: true,
+ add_group_by: [status],
+ filter: {
+ mode: `relative`,
+ include: [
+ {
+ member: `status`,
+ operator: `notEquals`,
+ values: [`archived`]
+ }
+ ]
+ }
+ }
+}
+```
+
+
+
+## Choosing between `mode: relative` and `mode: fixed`
+
+| Mode | Inherits query filters? | Inherits outer multi-stage filters? | Use when |
+|---|---|---|---|
+| `relative` (default) | Yes (minus `exclude` / `keep_only`) | Yes | The measure should react to the surrounding query; you only need to tweak a few filters. |
+| `fixed` | No | No | The measure must always represent the same slice — baselines, denominators in chained ratios, fixed cohorts. |
+
+`relative` mode pairs naturally with `exclude` / `keep_only` / `include`
+overrides. `fixed` mode typically uses only `include`, because there's no
+inherited state to remove.
+
+## See also
+
+- [`filter` parameter on measures][ref-filter-directives]
+- [`filter` parameter on dimensions][ref-dim-filter]
+- [Multi-stage measures][ref-multi-stage]
+- [Calculating share of total][ref-share-of-total]
+- [Calculating period-over-period changes][ref-period-over-period]
+
+[ref-filter-directives]: /reference/data-modeling/measures#filter
+[ref-dim-filter]: /reference/data-modeling/dimensions#filter
+[ref-multi-stage]: /docs/data-modeling/measures#multi-stage-measures
+[ref-share-of-total]: /recipes/data-modeling/share-of-total
+[ref-period-over-period]: /recipes/data-modeling/period-over-period
+[ref-tesseract-env]: /reference/configuration/environment-variables#cubejs_tesseract_sql_planner
+[ref-rest-query-ops]: /reference/core-data-apis/rest-api/query-format#filters-operators
+[link-tesseract]: https://cube.dev/blog/introducing-next-generation-data-modeling-engine
From f08e56edcc8d3c564778aa683fe044530281d224 Mon Sep 17 00:00:00 2001
From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com>
Date: Fri, 5 Jun 2026 14:13:22 +0000
Subject: [PATCH 3/3] docs: surface filtering multi-stage measures recipe on
recipes home
---
docs-mintlify/recipes/index.mdx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/docs-mintlify/recipes/index.mdx b/docs-mintlify/recipes/index.mdx
index ac6e5eddd03e9..0fbff7ecf66cc 100644
--- a/docs-mintlify/recipes/index.mdx
+++ b/docs-mintlify/recipes/index.mdx
@@ -4,7 +4,7 @@ description: Step-by-step tutorials and best practices for getting the most out
mode: wide
---
-Explore **38 recipes** across data modeling, calculations, analytics patterns,
+Explore **39 recipes** across data modeling, calculations, analytics patterns,
pre-aggregations, configuration, APIs, and AI.
## Data Modeling
@@ -51,6 +51,9 @@ pre-aggregations, configuration, APIs, and AI.
Calculate week-over-week, month-over-month, and other period-over-period metric changes.
+
+ Use filter directives to drop, replace, or pin filters on multi-stage measures — for baselines, fixed cohorts, and chained calculations.
+
Let users select filter values and use them in calculations without filtering the entire query.