mikebridge commented on code in PR #44454:
URL: https://github.com/apache/superset/pull/44454#discussion_r4264061759


##########
superset/semantic_layers/mapper.py:
##########
@@ -432,59 +439,54 @@ def _map_query_object(
 
     grain = _convert_time_grain(query_object.extras.get("time_grain_sqla"))
     time_axis_column = _get_grain_time_axis_column(query_object, 
all_dimensions)
-    # A semantic view can expose multiple Dimension variants per name (one per
-    # supported time grain). Pick exactly one variant per selected column:
-    # for the time-axis column we honor the user's grain selection, falling
-    # back to the raw / no-grain variant when no exact match exists and then
-    # to any available variant so the axis is never silently dropped; for
-    # every other selected column we prefer the raw variant and otherwise
-    # take any available variant.
+    grouping_grains: dict[str, Grain | None] = (
+        {time_axis_column: grain}
+        if time_axis_column is not None and time_axis_column in 
normalized_columns
+        else {}
+    )
+    grouping_dimensions: dict[str, Dimension] = resolve_dimensions(
+        view_dimensions, usage=DimensionUsage.GROUP, 
grouping_grains=grouping_grains
+    )
     dimensions: list[Dimension] = []
-    seen_non_axis: dict[str, Dimension] = {}
-    axis_variants: list[Dimension] = []
-    axis_match: Dimension | None = None
-    for dimension in view_dimensions:
-        if dimension.name not in normalized_columns:
-            continue
-        if dimension.name == time_axis_column:
-            axis_variants.append(dimension)
-            if axis_match is None and dimension.grain == grain:
-                axis_match = dimension
-            continue
-        existing = seen_non_axis.get(dimension.name)
-        if existing is None or (existing.grain is not None and dimension.grain 
is None):
-            seen_non_axis[dimension.name] = dimension
-
-    if axis_match is not None:
-        dimensions.append(axis_match)
-    elif axis_variants:
-        # No variant matches the requested grain. Prefer the raw (grain=None)
-        # variant; otherwise pick a deterministic fallback so the axis stays
-        # on the query instead of being silently dropped.
-        raw_variant = next((v for v in axis_variants if v.grain is None), None)
-        dimensions.append(
-            raw_variant
-            if raw_variant is not None
-            else min(axis_variants, key=lambda v: v.grain.name if v.grain else 
"")
-        )
-    dimensions.extend(seen_non_axis.values())
-
-    order = _get_order_from_query_object(query_object, all_metrics, 
all_dimensions)
+    if (
+        time_axis_column in normalized_columns
+        and time_axis_column in grouping_dimensions
+    ):
+        dimensions.append(grouping_dimensions[time_axis_column])
+    dimensions.extend(
+        dimension
+        for name, dimension in grouping_dimensions.items()

Review Comment:
   Good catch — the merge in 5ec25a5726 retained your requested-order 
comprehension. In f808d1e312 I've added the exact `region, category` case plus 
a duplicate-column variant: both fail when the old catalog-order block is 
restored and pass with the merged block. The raw/month grain tests remain 
intact; 410 resolver/mapper tests and all 1,394 semantic-layer tests pass. 
Implemented with Codex; independently reviewed by Claude.
   



##########
superset/semantic_layers/dimension_resolution.py:
##########
@@ -0,0 +1,118 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Resolve immutable provider dimensions for each query usage."""
+
+from collections.abc import Iterable, Mapping
+from enum import Enum
+
+from flask_babel import gettext as _
+from superset_core.semantic_layers.types import Dimension, Grain
+
+from superset.exceptions import QueryObjectValidationError
+
+# Known grains rank finest to coarsest, ahead of unknown representations.
+_GRAIN_FINENESS: dict[str, int] = {
+    "PT1S": 0,
+    "PT1M": 1,
+    "PT1H": 2,
+    "P1D": 3,
+    "P1W": 4,
+    "P1M": 5,
+    "P3M": 6,
+    "P1Y": 7,
+}
+
+
+class DimensionUsage(Enum):
+    """Keep selection policy explicit at each host/provider boundary."""
+
+    METADATA = "metadata"
+    COMPATIBILITY = "compatibility"
+    FILTER = "filter"
+    TIME_BOUND = "time_bound"

Review Comment:
   Good catch — removed the unused `TIME_BOUND` member in f808d1e312. The six 
roles with production callers remain covered by the resolver tests; no mapper 
behavior changed in this follow-up. Implemented with Codex; independently 
reviewed by Claude. @aminghadersohi, could you take another look at this small 
enum cleanup and the added two-column ordering regression?
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to