Skip to main content
Version: Next

Semantic filter expressions

SemanticQuery.filters and GroupLimit.filters are AND sets of FilterExpression = Filter | OrFilter. OrFilter is a frozen group of at least two distinct Filter leaves from the same predicate stage (WHERE or HAVING). Nested groups are unsupported. Preserve parentheses and parameterize every leaf. Render leaves in a deterministic order, keeping bound values paired with their predicates; frozenset iteration is not stable across processes.

The host normalizes UI <NULL> and <empty string> sentinels before coercing values. NULL equality becomes IS_NULL (inequality becomes IS_NOT_NULL). Mixed positive membership becomes OrFilter(IN(non_null_values), IS_NULL); mixed negative membership becomes two AND leaves, NOT_IN and IS_NOT_NULL. Empty membership and NULL comparison/LIKE operands are rejected before provider calls. Scalar comparisons use the first collection value, or NULL for an empty collection, matching native datasources.

The host's superset_typing.FilterValues permits None inside list selections and variable-length tuples. SDK FilterValues also includes None.

Provider compatibility​

OrFilter is mandatory core vocabulary, not an optional capability. Every provider must render groups for main queries, row counts and group-limit subqueries. Upgrade adapters before, or atomically with, the host/SDK upgrade; leaf-only adapters cannot consume mixed positive NULL selections. get_values retains set[Filter] | None; its host caller supplies only a LIKE leaf or no filter.

Query and group-limit readers need isinstance narrowing before accessing leaf attributes such as operator or column. Upgrading the SDK alone does not add adapter support. Pin and test a compatible host/SDK with the adapter. The host does not check a capability before sending groups. Never silently skip an OrFilter: doing so drops a query constraint. An un-upgraded adapter can fail during execution rather than return a host validation error.

Adapters supporting older SDKs must guard the type import, including during rollback. Do not unconditionally import OrFilter at module load on those hosts:

from superset_core.semantic_layers import types as semantic_types

or_filter_type: type | None = getattr(semantic_types, "OrFilter", None)

Use the guarded type in runtime dispatch (or_filter_type is not None followed by isinstance(expression, or_filter_type)). Grouped rendering must be implemented before deploying against an SDK that emits groups. Keep annotations that reference new union types behind TYPE_CHECKING or in a version-specific adapter module so older SDKs can still load the legacy leaf path.

Result caches​

The host includes semantic-null-filters-v1 in semantic result-cache keys. This separates legacy answers during rolling deployment without flushing unrelated caches. Keep this protocol marker alongside any metadata-generation cache keys.

Containment-cache identities use format v5, separating grouped predicates from older leaf-only entries during rolling upgrades. OR groups require identical canonical group sets for reuse; they are not flattened or used in subset proofs or leftover row filtering. Queries with grouped predicates do not use rollup reuse.