Skip to content

Associative filters

9 min read · Updated 30 Oct 2026

An associative filter selects entities by their related records. For example, you can list customers who bought Bikes and have no Blue purchases, then include customers from the US as an additional group. Ordinary filters test rows; associative filters combine sets of entities.

In the editor, associative filters are labelled Set Filter. In a Business Question, open Filters → Set Filter, choose a target field, and add intersection or union branches. Each branch uses the ordinary condition builder, including nested AND/OR groups, typed values, date conditions and parameters. Not excludes the whole association. The selected field is retained when you reopen the question.

In the Data Explorer, the Filter menu has a collapsible Set Filter section. Use it to view, edit, define or clear a set filter for the preview; changes take effect when you select Apply. Clear set filter removes only the set filter and keeps your other filters.

You can also store a set filter on its own, so the same population (customers with lifetime sales of at least 3,000, say) is defined once. In Business Context → Filters, select Add → Set filter, name it, choose its target field and add branches exactly as in a Business Question. A new set filter is saved only once its target field is chosen and every branch has a condition. The SQL preview at the bottom of the panel shows the query that lists the set’s values.

Once the set filter is complete and the layer is saved online, the layer’s AI model writes its description from the branches and how they combine, for example “Customers who bought Bikes and Clothing”. While the panel is open, it rewrites the description as you change the branches until you edit the description yourself. A saved description is not rewritten when you open the panel again; clear it, then change a branch or reopen the panel, to have AI write a new one. The description is kept only when you select Create or Update.

The Sets diagram counts each branch and the combined result in the published layer. When the description reads “… who …”, the result line uses it: “3,172 of 18,484 who bought Bikes and Clothing”. A branch with a dotted underline has its own AI description; hover over or focus it to read it.

To use a named set filter, choose it in Set filter source. To use named ordinary conditions, choose them in Filter source. Each section accepts either an inline definition or a named reference. A Business Question can use both kinds independently, and ordinary filters inside an associative branch can also reference a definition. In the Data Explorer, the Filter menu offers Filter source above its conditions too; a stored filter chosen there applies to the preview and is kept when you save the exploration as a Business Question.

filters:
reference:
filter: EMEA sales
associativeFilters:
reference:
filter: Active customers

Definitions live in the root filters collection. Each has exactly one filters or associativeFilters body; no type is needed. Editing a named definition changes its consumers. Renaming follows nested references. Deleting a definition copies its resolved content into each consumer first; fix an invalid definition before deleting it this way.

Each picker lists only filters of its own kind and disables filters that require another join group. A reference to the wrong kind already in a document stays visible with an error. Invalid document references are retained and marked with errors, including missing names and reference cycles. Execution and publishing require those errors to be fixed.

Business Questions and Data Explorer show the set filter’s Population, stored or defined in place, once every branch has a condition and the expression is one the engine runs: “92 of 18,484 Customer Key values”. It is counted in the published layer, so a layer that is not saved online shows no Population. Select Show Venn diagram to count each branch and draw the sets.

reference: { view: Customers, field: Age } makes Customers the target. When a branch reads another table, such as Sales, the set keeps Customers rows, and joins in the semantic layer determine how related records connect to them. For such a set, Age records your field selection; it is not a join key, a grouping, or an implicit age condition, and selecting another field in Customers has the same membership meaning.

When every branch reads only the target table, there is no join to connect them, so the set is keyed on the fields of that table your question groups by, and the target field matters. Group such a question by the target field. Other fields of the target table can join it only when the target field is the key other tables join to the target on, such as Customers.Id. When Nash, an MCP client or the Business Questions API runs a question that groups the set by any other field of the target table, its key included, the question is refused: each group would pass whole when its own values meet the branches. The Data Explorer and the editor’s SQL preview do not refuse it. They run the set keyed on the fields the question groups by, so group such a question there by the target field. A set on Orders of “ordered in 1992” and “ordered in 1998” with O Custkey as its field, grouped by order status, would keep the orders whose status occurs in both years rather than the customers who ordered in both. Grouped by order key, it would keep no orders, since no order is placed in both years. To break such a set down by another field, target the entity’s own table through a join, or ask a separate question.

A negative association includes customers without matching related rows, including customers with no purchases. Your question’s own grouping, measures and ordinary filters can still narrow the final answer. For a customer list, select a customer dimension without adding an unnecessary measure.

This example assumes your layer defines Customers.Id, Customers.Age, Customers.Region, Sales.Category and Sales.Color, with a relationship from Sales to Customers:

dimensions:
- key: Customer
reference: { view: Customers, field: Id }
measures: []
associativeFilters:
reference: { view: Customers, field: Age }
expression: '({af1} AND NOT {af2}) OR {af3}'
af1:
filters:
expression: '{f1} OR {f2}'
f1:
operator: equals
arguments:
- reference: { view: Sales, field: Category }
- value: Bikes
f2:
operator: equals
arguments:
- reference: { view: Sales, field: Category }
- value: Accessories
af2:
filters:
expression: '{f1}'
f1:
operator: equals
arguments:
- reference: { view: Sales, field: Color }
- value: Blue
af3:
filters:
expression: '{f1}'
f1:
operator: contains
arguments:
- reference: { view: Customers, field: Region }
- value: US

The final union can include US customers with Blue purchases. NOT {af2} applies only to the intersection, not as an exclusion after the union. Inside af1, either condition can match. Separate intersection branches may match different related records.

Generated association names are af1, af2, …, which the editor displays as Set 1, Set 2, …; generated condition names are f1, f2, …. You can choose custom names and they survive saving. Names are case-sensitive, cannot contain braces or line breaks, and must occur exactly once in their own expression. expression and reference are reserved in an association block; expression and parameters are reserved in an ordinary filter. Empty or whitespace-only names are invalid.

The supported outer shape is (A AND B AND …) OR C OR D …, where any individual branch can use NOT. A single branch, negative-only branch, intersection-only, or union-only expression is valid. Multiple intersection groups joined by OR, AND over OR groups, and group-wide NOT are rejected. The ordinary conditions inside each branch retain nested AND/OR support; ordinary row predicates do not accept unary NOT.

A measure filter keeps the target entities whose aggregate over the set’s related rows passes a condition. For example, a set on Customers with Sales.Category equals Bikes and Net Sales greaterThan 1000 keeps customers whose Bikes sales total more than 1000.

In the editor, each set has two sections. Filters test fields and dimensions; Measure filters test stored measures. The set always applies its filters first, then its measure filters to the rows they select, so the two sections are joined by AND. Either section can hold several rules and groups, combined with AND or OR within that section.

The editor and layer validation accept a decimal value in a measure filter, including on a measure that returns whole numbers, such as a count.

af1:
filters:
expression: '{f1} AND {f2}'
f1:
operator: equals
arguments:
- reference: { view: Sales, field: Category }
- value: Bikes
f2:
operator: greaterThan
arguments:
- reference: { measure: Net Sales }
- value: 1000

In the stored branch, the measure filters are one top-level AND part of the set’s conditions: ({f1} OR {f2}) AND ({f3} OR {f4}), where f3 and f4 test measures. Measure conditions may be grouped and ORed among themselves, but a group that mixes them with field or dimension conditions, or an OR between the two, is rejected. To include entities that meet either a measure filter or another condition, put each in its own union set.

A set can contain measure filters alone. It then relates its target through the fields its measures aggregate, so each of those measures must aggregate at least one field; otherwise add a row condition to the set. NOT on such a set keeps the entities whose aggregate does not pass.

A measure filter tests its measure once per target value, so the measure has to be one the query engine can break down by the target field. In the stored set filter panel, once a layer is saved online, the editor asks the published layer about each measure the set’s measure filters test. When the engine reports that a measure cannot be broken down by the target, the warning appears under that set with the engine’s message and suggested fixes, such as reviewing the joins between the measure’s view and the target’s. Such a set would test the measure as 0 or empty for every target value, so the set filter cannot be saved until the warning is resolved.

Your question’s ordinary filters apply outside the association subqueries. They are not silently inherited by each branch. Put a date restriction inside a branch when membership should be limited to that period. Mandatory semantic-layer security and view filters still apply.

Branch predicates accept field and ordinary dimension subjects, using the same validation and operator support as ordinary filters. A branch can also test a stored measure; see Measure filters in a set. For geoIntersects, a typed geography value is a closed coordinate ring, for example { value: "1 1, 2 1, 2 2, 1 2, 1 1", dataType: geography }, using longitude/latitude pairs. It is not GeoJSON text. Imported rectangular selections preserve their shape as a closed ring.

An optional branch via names an existing correlation root; it does not define join keys. A missing or ambiguous correlation produces an error instead of an answer with a filter omitted.

For runtime parameter values, use parameters.associativeFilters[branchName][parameterName]. Declare typed defaults in that branch’s filters.parameters. Overrides belong to an execution and are not saved in the question. See Business Questions for running, saving and publishing a question.

Explicit associative filters are separate from the automatic association setting. They use the same definition for stored and unsaved questions, HTTP queries, MCP and Nash. There is no separate Business Question API in the SDK.

Associative filters are part of the V3 Business Question contract. Measure filters in a set require MCP contract 6.3 or later. Refresh cached tool schemas before authoring. Existing associative branches survive V2-to-V3 migration. Older clients that cannot read these properties must be updated before editing migrated questions. See the complete YAML reference.