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

beto pushed a commit to branch explorable
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/explorable by this push:
     new 69aa488c78 Adding get_dataframe
69aa488c78 is described below

commit 69aa488c78a04df85aa8c3efcf59465f42d24ac1
Author: Beto Dealmeida <[email protected]>
AuthorDate: Tue Oct 14 17:09:02 2025 -0400

    Adding get_dataframe
---
 superset/semantic_layers/snowflake_.py | 130 +++++++++++++++++++++++----------
 superset/semantic_layers/types.py      |   4 +-
 2 files changed, 94 insertions(+), 40 deletions(-)

diff --git a/superset/semantic_layers/snowflake_.py 
b/superset/semantic_layers/snowflake_.py
index be1dab9414..40b8cf52b6 100644
--- a/superset/semantic_layers/snowflake_.py
+++ b/superset/semantic_layers/snowflake_.py
@@ -24,6 +24,7 @@ from typing import Any, Literal, Union
 
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives import serialization
+from pandas import DataFrame
 from pydantic import (
     BaseModel,
     ConfigDict,
@@ -36,6 +37,7 @@ from snowflake.connector import connect, DictCursor
 from snowflake.connector.connection import SnowflakeConnection
 from snowflake.sqlalchemy.snowdialect import SnowflakeDialect
 
+from superset.common.query_object import QueryObject
 from superset.semantic_layers.types import (
     BINARY,
     BOOLEAN,
@@ -44,11 +46,13 @@ from superset.semantic_layers.types import (
     DECIMAL,
     Dimension,
     Filter,
+    FilterValues,
     INTEGER,
     Metric,
     NativeFilter,
     NUMBER,
     OBJECT,
+    Operator,
     STRING,
     TIME,
     Type,
@@ -437,44 +441,6 @@ class SnowflakeExplorable:
 
         return metrics
 
-    def get_values(
-        self,
-        dimension: Dimension,
-        filters: set[Filter | NativeFilter] | None = None,
-    ) -> set[Any]:
-        """
-        Return distinct values for a dimension.
-        """
-        native_filters = {
-            (
-                filter_
-                if isinstance(filter_, NativeFilter)
-                else self._build_native_filter(filter_)
-            )
-            for filter_ in (filters or set())
-        }
-        parenthesized = {f"({filter_.definition})" for filter_ in 
native_filters}
-        predicates = f"WHERE {' AND '.join(parenthesized)}" if parenthesized 
else ""
-
-        query = f"""
-            SELECT {self._quote(dimension.name)}
-            FROM
-                SEMANTIC_VIEW(
-                    {self.uid()}
-                    DIMENSIONS {self._quote(dimension.id)}
-                )
-            {predicates}
-        """  # noqa: S608
-        connection_parameters = get_connection_parameters(self.configuration)
-        with connect(**connection_parameters) as connection:
-            cursor = connection.cursor(DictCursor)
-            return {row[0] for row in cursor.execute(query)}
-
-    def _build_native_filter_(self, filter_: Filter) -> NativeFilter:
-        """
-        Convert a Filter to a NativeFilter.
-        """
-
     def _get_type(self, snowflake_type: str | None) -> type[Type]:
         """
         Return the semantic type corresponding to a Snowflake type.
@@ -502,6 +468,74 @@ class SnowflakeExplorable:
 
         return STRING
 
+    def get_values(
+        self,
+        dimension: Dimension,
+        filters: set[Filter | NativeFilter] | None = None,
+    ) -> set[Any]:
+        """
+        Return distinct values for a dimension.
+        """
+        # convert filters predicate with associated parameters; native filters 
are
+        # already strings, so we keep them as-is
+        unary_operators = {Operator.IS_NULL, Operator.IS_NOT_NULL}
+        predicates: list[str] = []
+        parameters: list[FilterValues] = []
+        for filter_ in filters or set():
+            if isinstance(filter, NativeFilter):
+                predicates.append(f"({filter_.definition})")
+            else:
+                predicates.append(f"({self._build_predicate(filter_)})")
+                if filter_.operator not in unary_operators:
+                    parameters.extend(
+                        [filter_.value]
+                        if not isinstance(filter_.value, frozenset)
+                        else filter_.value
+                    )
+
+        query = f"""
+            SELECT {self._quote(dimension.name)}
+            FROM
+                SEMANTIC_VIEW(
+                    {self.uid()}
+                    DIMENSIONS {dimension.id}
+                )
+            {"WHERE " + " AND ".join(predicates) if predicates else ""}
+        """  # noqa: S608
+        connection_parameters = get_connection_parameters(self.configuration)
+        with connect(**connection_parameters) as connection:
+            cursor = connection.cursor()
+            return {row[0] for row in cursor.execute(query, tuple(parameters))}
+
+    def _build_predicate(self, filter_: Filter) -> str:
+        """
+        Convert a Filter to a NativeFilter.
+        """
+        column = filter_.column
+        operator = filter_.operator
+        value = filter_.value
+
+        column_name = self._quote(column.name)
+
+        # Handle IS NULL and IS NOT NULL operators (no value needed)
+        if operator in {Operator.IS_NULL, Operator.IS_NOT_NULL}:
+            return f"{column_name} {operator.value}"
+
+        # Handle IN and NOT IN operators (set values)
+        if operator in {Operator.IN, Operator.NOT_IN}:
+            if not isinstance(value, frozenset):
+                value = {value}
+            formatted_values = ", ".join("?" for _ in value)
+            return f"{column_name} {operator.value} ({formatted_values})"
+
+        return f"{column_name} {operator.value} ?"
+
+    def get_dataframe(self, query_object: QueryObject) -> DataFrame:
+        """
+        Execute a query and return the results as a Pandas DataFrame.
+        """
+        pass
+
     __repr__ = uid
 
 
@@ -546,3 +580,23 @@ if __name__ == "__main__":
     print("=======")
     for metric in explorable.get_metrics():
         print(metric)
+    print("VALUES")
+    print("======")
+    dimension = Dimension(
+        id="ITEM.CATEGORY",
+        name="CATEGORY",
+        type=STRING,
+        description=None,
+        definition="I_CATEGORY",
+        grain=None,
+    )
+    print(explorable.get_values(dimension))
+    filters = {
+        Filter(dimension, Operator.IS_NOT_NULL, None),
+        Filter(dimension, Operator.NOT_EQUALS, "Books"),
+    }
+    print(explorable.get_values(dimension, filters))
+    filters = {
+        Filter(dimension, Operator.IN, frozenset({"Children", "Electronics"})),
+    }
+    print(explorable.get_values(dimension, filters))
diff --git a/superset/semantic_layers/types.py 
b/superset/semantic_layers/types.py
index eb4ef5e341..0e76703920 100644
--- a/superset/semantic_layers/types.py
+++ b/superset/semantic_layers/types.py
@@ -178,14 +178,14 @@ class Operator(enum.Enum):
     IS_NOT_NULL = "IS NOT NULL"
 
 
-FilterTypes = str | int | float | bool | datetime | date | time | timedelta | 
None
+FilterValues = str | int | float | bool | datetime | date | time | timedelta | 
None
 
 
 @dataclass(frozen=True)
 class Filter:
     column: Dimension | Metric
     operator: Operator
-    value: FilterTypes | set[FilterTypes]
+    value: FilterValues | set[FilterValues]
 
 
 @dataclass(frozen=True)

Reply via email to