This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch semantic-layer-remove-adhocfilter
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 08de4dbb5acd4ce295c456d743dd5582e72d116f
Author: Beto Dealmeida <[email protected]>
AuthorDate: Tue Feb 10 09:55:45 2026 -0500

    chore: remove AdhocFilter
---
 .../superset_core/semantic_layers/semantic_view.py |  7 ++-
 .../src/superset_core/semantic_layers/types.py     | 15 ++----
 superset/semantic_layers/mapper.py                 | 41 +++++++++-------
 tests/unit_tests/semantic_layers/mapper_test.py    | 56 ++++++++++++++++------
 4 files changed, 73 insertions(+), 46 deletions(-)

diff --git a/superset-core/src/superset_core/semantic_layers/semantic_view.py 
b/superset-core/src/superset_core/semantic_layers/semantic_view.py
index cdbca096af8..a67050f5b14 100644
--- a/superset-core/src/superset_core/semantic_layers/semantic_view.py
+++ b/superset-core/src/superset_core/semantic_layers/semantic_view.py
@@ -21,7 +21,6 @@ import enum
 from typing import Protocol, runtime_checkable
 
 from superset_core.semantic_layers.types import (
-    AdhocFilter,
     Dimension,
     Filter,
     GroupLimit,
@@ -69,7 +68,7 @@ class SemanticView(Protocol):
     def get_values(
         self,
         dimension: Dimension,
-        filters: set[Filter | AdhocFilter] | None = None,
+        filters: set[Filter] | None = None,
     ) -> SemanticResult:
         """
         Return distinct values for a dimension.
@@ -79,7 +78,7 @@ class SemanticView(Protocol):
         self,
         metrics: list[Metric],
         dimensions: list[Dimension],
-        filters: set[Filter | AdhocFilter] | None = None,
+        filters: set[Filter] | None = None,
         order: list[OrderTuple] | None = None,
         limit: int | None = None,
         offset: int | None = None,
@@ -94,7 +93,7 @@ class SemanticView(Protocol):
         self,
         metrics: list[Metric],
         dimensions: list[Dimension],
-        filters: set[Filter | AdhocFilter] | None = None,
+        filters: set[Filter] | None = None,
         order: list[OrderTuple] | None = None,
         limit: int | None = None,
         offset: int | None = None,
diff --git a/superset-core/src/superset_core/semantic_layers/types.py 
b/superset-core/src/superset_core/semantic_layers/types.py
index 46bcf707174..4043e9a1c62 100644
--- a/superset-core/src/superset_core/semantic_layers/types.py
+++ b/superset-core/src/superset_core/semantic_layers/types.py
@@ -239,6 +239,7 @@ class Operator(str, enum.Enum):
     NOT_LIKE = "NOT LIKE"
     IS_NULL = "IS NULL"
     IS_NOT_NULL = "IS NOT NULL"
+    ADHOC = "ADHOC"
 
 
 FilterValues = str | int | float | bool | datetime | date | time | timedelta | 
None
@@ -252,19 +253,11 @@ class PredicateType(enum.Enum):
 @dataclass(frozen=True, order=True)
 class Filter:
     type: PredicateType
-    column: Dimension | Metric
+    column: Dimension | Metric | None
     operator: Operator
     value: FilterValues | frozenset[FilterValues]
 
 
-# TODO (betodealmeida): convert into Operator:
-# Filter(type=..., column=None, operator=Operator.AdHoc, value="some 
definition")
-@dataclass(frozen=True, order=True)
-class AdhocFilter:
-    type: PredicateType
-    definition: str
-
-
 class OrderDirection(enum.Enum):
     ASC = "ASC"
     DESC = "DESC"
@@ -291,7 +284,7 @@ class GroupLimit:
     metric: Metric | None
     direction: OrderDirection = OrderDirection.DESC
     group_others: bool = False
-    filters: set[Filter | AdhocFilter] | None = None
+    filters: set[Filter] | None = None
 
 
 @dataclass(frozen=True)
@@ -328,7 +321,7 @@ class SemanticQuery:
 
     metrics: list[Metric]
     dimensions: list[Dimension]
-    filters: set[Filter | AdhocFilter] | None = None
+    filters: set[Filter] | None = None
     order: list[OrderTuple] | None = None
     limit: int | None = None
     offset: int | None = None
diff --git a/superset/semantic_layers/mapper.py 
b/superset/semantic_layers/mapper.py
index 1232afbb477..441e48cf941 100644
--- a/superset/semantic_layers/mapper.py
+++ b/superset/semantic_layers/mapper.py
@@ -32,7 +32,6 @@ import numpy as np
 from superset_core.semantic_layers.semantic_view import SemanticViewFeature
 from superset_core.semantic_layers.types import (
     AdhocExpression,
-    AdhocFilter,
     Day,
     Dimension,
     Filter,
@@ -370,14 +369,14 @@ def _get_filters_from_query_object(
     query_object: ValidatedQueryObject,
     time_offset: str | None,
     all_dimensions: dict[str, Dimension],
-) -> set[Filter | AdhocFilter]:
+) -> set[Filter]:
     """
     Extract all filters from the query object, including time range filters.
 
     This simplifies the complexity of 
from_dttm/to_dttm/inner_from_dttm/inner_to_dttm
     by converting all time constraints into filters.
     """
-    filters: set[Filter | AdhocFilter] = set()
+    filters: set[Filter] = set()
 
     # 1. Add fetch values predicate if present
     if (
@@ -385,9 +384,11 @@ def _get_filters_from_query_object(
         and query_object.datasource.fetch_values_predicate
     ):
         filters.add(
-            AdhocFilter(
+            Filter(
                 type=PredicateType.WHERE,
-                definition=query_object.datasource.fetch_values_predicate,
+                column=None,
+                operator=Operator.ADHOC,
+                value=query_object.datasource.fetch_values_predicate,
             )
         )
 
@@ -415,7 +416,7 @@ def _get_filters_from_query_object(
     return filters
 
 
-def _get_filters_from_extras(extras: dict[str, Any]) -> set[AdhocFilter]:
+def _get_filters_from_extras(extras: dict[str, Any]) -> set[Filter]:
     """
     Extract filters from the extras dict.
 
@@ -430,25 +431,29 @@ def _get_filters_from_extras(extras: dict[str, Any]) -> 
set[AdhocFilter]:
       Handled in _convert_time_grain() and used for dimension grain matching
 
     Note: The WHERE and HAVING clauses from extras are SQL expressions that
-    are passed through as-is to the semantic layer as AdhocFilter objects.
+    are passed through as-is to the semantic layer as adhoc Filter objects.
     """
-    filters: set[AdhocFilter] = set()
+    filters: set[Filter] = set()
 
     # Add WHERE clause from extras
     if where_clause := extras.get("where"):
         filters.add(
-            AdhocFilter(
+            Filter(
                 type=PredicateType.WHERE,
-                definition=where_clause,
+                column=None,
+                operator=Operator.ADHOC,
+                value=where_clause,
             )
         )
 
     # Add HAVING clause from extras
     if having_clause := extras.get("having"):
         filters.add(
-            AdhocFilter(
+            Filter(
                 type=PredicateType.HAVING,
-                definition=having_clause,
+                column=None,
+                operator=Operator.ADHOC,
+                value=having_clause,
             )
         )
 
@@ -540,7 +545,7 @@ def _convert_query_object_filter(
     all_dimensions: dict[str, Dimension],
 ) -> set[Filter] | None:
     """
-    Convert a QueryObject filter dict to a semantic layer Filter or 
AdhocFilter.
+    Convert a QueryObject filter dict to a semantic layer Filter.
     """
     operator_str = filter_["op"]
 
@@ -676,7 +681,7 @@ def _get_group_limit_from_query_object(
 def _get_group_limit_filters(
     query_object: ValidatedQueryObject,
     all_dimensions: dict[str, Dimension],
-) -> set[Filter | AdhocFilter] | None:
+) -> set[Filter] | None:
     """
     Get separate filters for the group limit subquery if needed.
 
@@ -699,7 +704,7 @@ def _get_group_limit_filters(
         return None
 
     # Create separate filters for the group limit subquery
-    filters: set[Filter | AdhocFilter] = set()
+    filters: set[Filter] = set()
 
     # Add time range filter using inner bounds
     if query_object.granularity:
@@ -732,9 +737,11 @@ def _get_group_limit_filters(
         and query_object.datasource.fetch_values_predicate
     ):
         filters.add(
-            AdhocFilter(
+            Filter(
                 type=PredicateType.WHERE,
-                definition=query_object.datasource.fetch_values_predicate,
+                column=None,
+                operator=Operator.ADHOC,
+                value=query_object.datasource.fetch_values_predicate,
             )
         )
 
diff --git a/tests/unit_tests/semantic_layers/mapper_test.py 
b/tests/unit_tests/semantic_layers/mapper_test.py
index 8e5dd06fe58..06cf6871ad5 100644
--- a/tests/unit_tests/semantic_layers/mapper_test.py
+++ b/tests/unit_tests/semantic_layers/mapper_test.py
@@ -24,7 +24,6 @@ from pytest_mock import MockerFixture
 from superset_core.semantic_layers.semantic_view import SemanticViewFeature
 from superset_core.semantic_layers.types import (
     AdhocExpression,
-    AdhocFilter,
     Day,
     Dimension,
     Filter,
@@ -202,9 +201,11 @@ def test_get_filters_from_extras_where() -> None:
 
     assert len(result) == 1
     filter_ = next(iter(result))
-    assert isinstance(filter_, AdhocFilter)
+    assert isinstance(filter_, Filter)
     assert filter_.type == PredicateType.WHERE
-    assert filter_.definition == "customer_id > 100"
+    assert filter_.column is None
+    assert filter_.operator == Operator.ADHOC
+    assert filter_.value == "customer_id > 100"
 
 
 def test_get_filters_from_extras_having() -> None:
@@ -215,7 +216,12 @@ def test_get_filters_from_extras_having() -> None:
     result = _get_filters_from_extras(extras)
 
     assert result == {
-        AdhocFilter(type=PredicateType.HAVING, definition="SUM(sales) > 1000"),
+        Filter(
+            type=PredicateType.HAVING,
+            column=None,
+            operator=Operator.ADHOC,
+            value="SUM(sales) > 1000",
+        ),
     }
 
 
@@ -230,8 +236,18 @@ def test_get_filters_from_extras_both() -> None:
     result = _get_filters_from_extras(extras)
 
     assert result == {
-        AdhocFilter(type=PredicateType.WHERE, definition="region = 'US'"),
-        AdhocFilter(type=PredicateType.HAVING, definition="COUNT(*) > 10"),
+        Filter(
+            type=PredicateType.WHERE,
+            column=None,
+            operator=Operator.ADHOC,
+            value="region = 'US'",
+        ),
+        Filter(
+            type=PredicateType.HAVING,
+            column=None,
+            operator=Operator.ADHOC,
+            value="COUNT(*) > 10",
+        ),
     }
 
 
@@ -450,9 +466,11 @@ def 
test_get_filters_from_query_object_with_extras(mock_datasource: MagicMock) -
             operator=Operator.LESS_THAN,
             value=datetime(2025, 10, 22),
         ),
-        AdhocFilter(
+        Filter(
             type=PredicateType.WHERE,
-            definition="customer_id > 100",
+            column=None,
+            operator=Operator.ADHOC,
+            value="customer_id > 100",
         ),
     }
 
@@ -494,9 +512,11 @@ def test_get_filters_from_query_object_with_fetch_values(
             operator=Operator.LESS_THAN,
             value=datetime(2025, 10, 22),
         ),
-        AdhocFilter(
+        Filter(
             type=PredicateType.WHERE,
-            definition="tenant_id = 123",
+            column=None,
+            operator=Operator.ADHOC,
+            value="tenant_id = 123",
         ),
     }
 
@@ -796,9 +816,11 @@ def 
test_get_group_limit_filters_with_extras(mock_datasource: MagicMock) -> None
             operator=Operator.LESS_THAN,
             value=datetime(2025, 10, 22),
         ),
-        AdhocFilter(
+        Filter(
             type=PredicateType.WHERE,
-            definition="customer_id > 100",
+            column=None,
+            operator=Operator.ADHOC,
+            value="customer_id > 100",
         ),
     }
 
@@ -2019,9 +2041,11 @@ def 
test_get_group_limit_filters_with_fetch_values_predicate(
 
     assert result is not None
     assert (
-        AdhocFilter(
+        Filter(
             type=PredicateType.WHERE,
-            definition="tenant_id = 123",
+            column=None,
+            operator=Operator.ADHOC,
+            value="tenant_id = 123",
         )
         in result
     )
@@ -2372,6 +2396,7 @@ def test_get_filters_from_query_object_with_filter_loop(
         f
         for f in result
         if isinstance(f, Filter)
+        and f.column
         and f.column.name == "category"
         and f.operator == Operator.EQUALS
     ]
@@ -2444,6 +2469,7 @@ def test_get_group_limit_filters_with_filter_loop(
         f
         for f in result
         if isinstance(f, Filter)
+        and f.column
         and f.column.name == "category"
         and f.operator == Operator.EQUALS
     ]
@@ -2555,6 +2581,7 @@ def 
test_get_filters_from_query_object_filter_returns_none(
         f
         for f in result
         if isinstance(f, Filter)
+        and f.column
         and f.column.name == "category"
         and f.operator == Operator.EQUALS
     ]
@@ -2607,6 +2634,7 @@ def test_get_group_limit_filters_filter_returns_none(
         f
         for f in result
         if isinstance(f, Filter)
+        and f.column
         and f.column.name == "category"
         and f.operator == Operator.EQUALS
     ]

Reply via email to