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.
Provider compatibility
OR_FILTERS is off by default. Declare it only after implementing groups for
main queries, row counts and group-limit subqueries. Existing leaf-only adapters
continue to work, but mixed positive NULL selections return a query validation
error (HTTP 400) until the adapter opts in. 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.
Adapters supporting older SDKs must guard both the type import and capability
lookup, including during rollback. Do not unconditionally import OrFilter or
access SemanticViewFeature.OR_FILTERS at module load on those hosts:
from superset_core.semantic_layers import types as semantic_types
from superset_core.semantic_layers.view import SemanticViewFeature
or_filter_type: type | None = getattr(semantic_types, "OrFilter", None)
or_filters_feature: SemanticViewFeature | None = getattr(
SemanticViewFeature, "OR_FILTERS", None
)
Use the guarded type in runtime dispatch (or_filter_type is not None followed
by isinstance(expression, or_filter_type)). Only advertise the capability when
both lookups succeed and grouped rendering is implemented. 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.