diff --git a/metricflow-semantics/metricflow_semantics/naming/dunder_scheme.py b/metricflow-semantics/metricflow_semantics/naming/dunder_scheme.py index e0e3c69711..0f0f239050 100644 --- a/metricflow-semantics/metricflow_semantics/naming/dunder_scheme.py +++ b/metricflow-semantics/metricflow_semantics/naming/dunder_scheme.py @@ -9,6 +9,7 @@ from dbt_semantic_interfaces.type_enums.date_part import DatePart from typing_extensions import override +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.naming.naming_scheme import QueryItemNamingScheme from metricflow_semantics.specs.instance_spec import InstanceSpec from metricflow_semantics.specs.patterns.entity_link_pattern import ( @@ -50,7 +51,7 @@ def input_str(self, instance_spec: InstanceSpec) -> Optional[str]: return names[0] @override - def spec_pattern(self, input_str: str) -> EntityLinkPattern: + def spec_pattern(self, input_str: str, semantic_manifest_lookup: SemanticManifestLookup) -> EntityLinkPattern: if not self.input_str_follows_scheme(input_str): raise ValueError(f"{repr(input_str)} does not follow this scheme.") diff --git a/metricflow-semantics/metricflow_semantics/naming/metric_scheme.py b/metricflow-semantics/metricflow_semantics/naming/metric_scheme.py index 0473cd44ce..fcd9fe5ac9 100644 --- a/metricflow-semantics/metricflow_semantics/naming/metric_scheme.py +++ b/metricflow-semantics/metricflow_semantics/naming/metric_scheme.py @@ -5,6 +5,7 @@ from dbt_semantic_interfaces.references import MetricReference from typing_extensions import override +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.naming.naming_scheme import QueryItemNamingScheme from metricflow_semantics.specs.instance_spec import InstanceSpec from metricflow_semantics.specs.patterns.metric_pattern import MetricSpecPattern @@ -25,7 +26,7 @@ def input_str(self, instance_spec: InstanceSpec) -> Optional[str]: return names[0] @override - def spec_pattern(self, input_str: str) -> MetricSpecPattern: + def spec_pattern(self, input_str: str, semantic_manifest_lookup: SemanticManifestLookup) -> MetricSpecPattern: input_str = input_str.lower() if not self.input_str_follows_scheme(input_str): raise RuntimeError(f"{repr(input_str)} does not follow this scheme.") diff --git a/metricflow-semantics/metricflow_semantics/naming/naming_scheme.py b/metricflow-semantics/metricflow_semantics/naming/naming_scheme.py index 950ceaf680..f9110c01b8 100644 --- a/metricflow-semantics/metricflow_semantics/naming/naming_scheme.py +++ b/metricflow-semantics/metricflow_semantics/naming/naming_scheme.py @@ -3,6 +3,7 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Optional +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.specs.patterns.spec_pattern import SpecPattern if TYPE_CHECKING: @@ -29,11 +30,14 @@ def input_str(self, instance_spec: InstanceSpec) -> Optional[str]: pass @abstractmethod - def spec_pattern(self, input_str: str) -> SpecPattern: + def spec_pattern(self, input_str: str, semantic_manifest_lookup: SemanticManifestLookup) -> SpecPattern: """Given an input that follows this scheme, return a spec pattern that matches the described input. - If the input_str does not follow this scheme, raise a ValueError. In practice, input_str_follows_scheme() should - be called on the input_str beforehand. + This is used to generate suggestions from available group-by-items if the user specifies a group-by-item that is + invalid. + + If this scheme cannot accommodate the spec, return None. This is needed to handle unsupported cases in + DunderNamingScheme, such as DatePart, but naming schemes should otherwise be complete. """ pass diff --git a/metricflow-semantics/metricflow_semantics/naming/object_builder_scheme.py b/metricflow-semantics/metricflow_semantics/naming/object_builder_scheme.py index eef087c533..c425332afc 100644 --- a/metricflow-semantics/metricflow_semantics/naming/object_builder_scheme.py +++ b/metricflow-semantics/metricflow_semantics/naming/object_builder_scheme.py @@ -12,6 +12,7 @@ from dbt_semantic_interfaces.references import EntityReference from typing_extensions import override +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.naming.naming_scheme import QueryItemNamingScheme from metricflow_semantics.naming.object_builder_str import ObjectBuilderNameConverter from metricflow_semantics.specs.instance_spec import InstanceSpec @@ -36,7 +37,7 @@ def input_str(self, instance_spec: InstanceSpec) -> Optional[str]: return ObjectBuilderNameConverter.input_str_from_spec(instance_spec) @override - def spec_pattern(self, input_str: str) -> SpecPattern: + def spec_pattern(self, input_str: str, semantic_manifest_lookup: SemanticManifestLookup) -> SpecPattern: if not self.input_str_follows_scheme(input_str): raise ValueError( f"The specified input {repr(input_str)} does not match the input described by the object builder " diff --git a/metricflow-semantics/metricflow_semantics/protocols/query_parameter.py b/metricflow-semantics/metricflow_semantics/protocols/query_parameter.py index c8c15bfd87..2b6969a252 100644 --- a/metricflow-semantics/metricflow_semantics/protocols/query_parameter.py +++ b/metricflow-semantics/metricflow_semantics/protocols/query_parameter.py @@ -6,6 +6,7 @@ from dbt_semantic_interfaces.type_enums.date_part import DatePart if TYPE_CHECKING: + from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.query.resolver_inputs.query_resolver_inputs import ( ResolverInputForGroupByItem, ResolverInputForMetric, @@ -22,8 +23,9 @@ def name(self) -> str: """The name of the metric.""" raise NotImplementedError - @property - def query_resolver_input(self) -> ResolverInputForMetric: # noqa: D102 + def query_resolver_input( # noqa: D102 + self, semantic_manifest_lookup: SemanticManifestLookup + ) -> ResolverInputForMetric: raise NotImplementedError @@ -36,8 +38,9 @@ def name(self) -> str: """The name of the metric.""" raise NotImplementedError - @property - def query_resolver_input(self) -> ResolverInputForGroupByItem: # noqa: D102 + def query_resolver_input( # noqa: D102 + self, semantic_manifest_lookup: SemanticManifestLookup + ) -> ResolverInputForGroupByItem: raise NotImplementedError @@ -58,8 +61,9 @@ def date_part(self) -> Optional[DatePart]: """Date part to extract from the dimension.""" raise NotImplementedError - @property - def query_resolver_input(self) -> ResolverInputForGroupByItem: # noqa: D102 + def query_resolver_input( # noqa: D102 + self, semantic_manifest_lookup: SemanticManifestLookup + ) -> ResolverInputForGroupByItem: raise NotImplementedError @@ -80,8 +84,9 @@ def descending(self) -> bool: """Indicates if the order should be ascending or descending.""" raise NotImplementedError - @property - def query_resolver_input(self) -> ResolverInputForOrderByItem: # noqa: D102 + def query_resolver_input( # noqa: D102 + self, semantic_manifest_lookup: SemanticManifestLookup + ) -> ResolverInputForOrderByItem: raise NotImplementedError diff --git a/metricflow-semantics/metricflow_semantics/query/query_parser.py b/metricflow-semantics/metricflow_semantics/query/query_parser.py index d9ca3f81ef..308761cdc8 100644 --- a/metricflow-semantics/metricflow_semantics/query/query_parser.py +++ b/metricflow-semantics/metricflow_semantics/query/query_parser.py @@ -215,7 +215,9 @@ def _parse_order_by_names( ResolverInputForGroupByItem( input_obj=order_by_name, input_obj_naming_scheme=group_by_item_naming_scheme, - spec_pattern=group_by_item_naming_scheme.spec_pattern(order_by_name_without_prefix), + spec_pattern=group_by_item_naming_scheme.spec_pattern( + order_by_name_without_prefix, semantic_manifest_lookup=self._manifest_lookup + ), ) ) break @@ -226,7 +228,9 @@ def _parse_order_by_names( ResolverInputForMetric( input_obj=order_by_name, naming_scheme=metric_naming_scheme, - spec_pattern=metric_naming_scheme.spec_pattern(order_by_name_without_prefix), + spec_pattern=metric_naming_scheme.spec_pattern( + order_by_name_without_prefix, semantic_manifest_lookup=self._manifest_lookup + ), ) ) @@ -240,11 +244,14 @@ def _parse_order_by_names( return resolver_inputs - @staticmethod def _parse_order_by( + self, order_by: Sequence[OrderByQueryParameter], ) -> Sequence[ResolverInputForOrderByItem]: - return tuple(order_by_query_parameter.query_resolver_input for order_by_query_parameter in order_by) + return tuple( + order_by_query_parameter.query_resolver_input(semantic_manifest_lookup=self._manifest_lookup) + for order_by_query_parameter in order_by + ) @staticmethod def generate_error_message( @@ -370,7 +377,9 @@ def _parse_and_validate_query( resolver_input_for_metric = ResolverInputForMetric( input_obj=metric_name, naming_scheme=metric_naming_scheme, - spec_pattern=metric_naming_scheme.spec_pattern(metric_name), + spec_pattern=metric_naming_scheme.spec_pattern( + metric_name, semantic_manifest_lookup=self._manifest_lookup + ), ) resolver_inputs_for_metrics.append(resolver_input_for_metric) break @@ -388,14 +397,18 @@ def _parse_and_validate_query( ) for metric_query_parameter in metrics: - resolver_inputs_for_metrics.append(metric_query_parameter.query_resolver_input) + resolver_inputs_for_metrics.append( + metric_query_parameter.query_resolver_input(semantic_manifest_lookup=self._manifest_lookup) + ) resolver_inputs_for_group_by_items: List[ResolverInputForGroupByItem] = [] for group_by_name in group_by_names: resolver_input_for_group_by_item: Optional[MetricFlowQueryResolverInput] = None for group_by_item_naming_scheme in self._group_by_item_naming_schemes: if group_by_item_naming_scheme.input_str_follows_scheme(group_by_name): - spec_pattern = group_by_item_naming_scheme.spec_pattern(group_by_name) + spec_pattern = group_by_item_naming_scheme.spec_pattern( + group_by_name, semantic_manifest_lookup=self._manifest_lookup + ) resolver_input_for_group_by_item = ResolverInputForGroupByItem( input_obj=group_by_name, input_obj_naming_scheme=group_by_item_naming_scheme, @@ -424,7 +437,9 @@ def _parse_and_validate_query( ) for group_by_parameter in group_by: - resolver_input_for_group_by_parameter = group_by_parameter.query_resolver_input + resolver_input_for_group_by_parameter = group_by_parameter.query_resolver_input( + semantic_manifest_lookup=self._manifest_lookup + ) resolver_inputs_for_group_by_items.append(resolver_input_for_group_by_parameter) logger.info( "Converted group-by-item input:\n" @@ -454,7 +469,7 @@ def _parse_and_validate_query( order_by_names=order_by_names, ) ) - resolver_inputs_for_order_by.extend(MetricFlowQueryParser._parse_order_by(order_by=order_by)) + resolver_inputs_for_order_by.extend(self._parse_order_by(order_by=order_by)) resolver_input_for_limit = ResolverInputForLimit(limit=limit) resolver_input_for_min_max_only = ResolverInputForMinMaxOnly(min_max_only=min_max_only) diff --git a/metricflow-semantics/metricflow_semantics/specs/query_param_implementations.py b/metricflow-semantics/metricflow_semantics/specs/query_param_implementations.py index d13b2bcd05..791b9cea34 100644 --- a/metricflow-semantics/metricflow_semantics/specs/query_param_implementations.py +++ b/metricflow-semantics/metricflow_semantics/specs/query_param_implementations.py @@ -9,12 +9,15 @@ from dbt_semantic_interfaces.type_enums.time_granularity import TimeGranularity from typing_extensions import override +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.naming.linkable_spec_name import StructuredLinkableSpecName from metricflow_semantics.naming.metric_scheme import MetricNamingScheme from metricflow_semantics.naming.object_builder_scheme import ObjectBuilderNamingScheme from metricflow_semantics.protocols.query_parameter import ( DimensionOrEntityQueryParameter, InputOrderByParameter, + MetricQueryParameter, + OrderByQueryParameter, TimeDimensionQueryParameter, ) from metricflow_semantics.protocols.query_parameter import SavedQueryParameter as SavedQueryParameterProtocol @@ -41,8 +44,11 @@ def _implements_protocol(self) -> TimeDimensionQueryParameter: grain: Optional[TimeGranularity] = None date_part: Optional[DatePart] = None - @property - def query_resolver_input(self) -> ResolverInputForGroupByItem: # noqa: D102 + def query_resolver_input( # noqa: D102 + self, + semantic_manifest_lookup: SemanticManifestLookup, + ) -> ResolverInputForGroupByItem: + # TODO: [custom granularity] use manifest lookup to handle custom granularities fields_to_compare = [ ParameterSetField.ELEMENT_NAME, ParameterSetField.ENTITY_LINKS, @@ -81,8 +87,16 @@ class DimensionOrEntityParameter(ProtocolHint[DimensionOrEntityQueryParameter]): def _implements_protocol(self) -> DimensionOrEntityQueryParameter: return self - @property - def query_resolver_input(self) -> ResolverInputForGroupByItem: # noqa: D102 + def query_resolver_input(self, semantic_manifest_lookup: SemanticManifestLookup) -> ResolverInputForGroupByItem: + """Produces resolver input from a query parameter representing a dimension or entity. + + Note these parameters do not currently have a direct need for the semantic_manifest_lookup, but since these + can be lumped in with other items that do require it we keep this method signature consistent across + the class sets. + + TODO: Refine these query input classes so that this kind of thing is either enforced in self-documenting + ways or removed from the codebase + """ name_structure = StructuredLinkableSpecName.from_name(self.name.lower()) return ResolverInputForGroupByItem( @@ -105,33 +119,43 @@ def query_resolver_input(self) -> ResolverInputForGroupByItem: # noqa: D102 @dataclass(frozen=True) -class MetricParameter: +class MetricParameter(ProtocolHint[MetricQueryParameter]): """Metric requested in a query.""" name: str - @property - def query_resolver_input(self) -> ResolverInputForMetric: # noqa: D102 + @override + def _implements_protocol(self) -> MetricQueryParameter: + return self + + def query_resolver_input( # noqa: D102 + self, semantic_manifest_lookup: SemanticManifestLookup + ) -> ResolverInputForMetric: naming_scheme = MetricNamingScheme() return ResolverInputForMetric( input_obj=self, naming_scheme=naming_scheme, - spec_pattern=naming_scheme.spec_pattern(self.name), + spec_pattern=naming_scheme.spec_pattern(self.name, semantic_manifest_lookup=semantic_manifest_lookup), ) @dataclass(frozen=True) -class OrderByParameter: +class OrderByParameter(ProtocolHint[OrderByQueryParameter]): """Order by requested in a query.""" order_by: InputOrderByParameter descending: bool = False - @property - def query_resolver_input(self) -> ResolverInputForOrderByItem: # noqa: D102 + @override + def _implements_protocol(self) -> OrderByQueryParameter: + return self + + def query_resolver_input( # noqa: D102 + self, semantic_manifest_lookup: SemanticManifestLookup + ) -> ResolverInputForOrderByItem: return ResolverInputForOrderByItem( input_obj=self, - possible_inputs=(self.order_by.query_resolver_input,), + possible_inputs=(self.order_by.query_resolver_input(semantic_manifest_lookup=semantic_manifest_lookup),), descending=self.descending, ) diff --git a/metricflow-semantics/metricflow_semantics/test_helpers/snapshot_helpers.py b/metricflow-semantics/metricflow_semantics/test_helpers/snapshot_helpers.py index c2c68adc33..2091414002 100644 --- a/metricflow-semantics/metricflow_semantics/test_helpers/snapshot_helpers.py +++ b/metricflow-semantics/metricflow_semantics/test_helpers/snapshot_helpers.py @@ -343,9 +343,11 @@ def assert_spec_set_snapshot_equal( # noqa: D103 def assert_linkable_spec_set_snapshot_equal( # noqa: D103 - request: FixtureRequest, mf_test_configuration: SnapshotConfiguration, set_id: str, spec_set: LinkableSpecSet + request: FixtureRequest, + mf_test_configuration: SnapshotConfiguration, + set_id: str, + spec_set: LinkableSpecSet, ) -> None: - # TODO: This will be used in a later PR and this message will be removed. naming_scheme = ObjectBuilderNamingScheme() assert_snapshot_text_equal( request=request, diff --git a/metricflow-semantics/tests_metricflow_semantics/model/test_where_filter_spec.py b/metricflow-semantics/tests_metricflow_semantics/model/test_where_filter_spec.py index 0fe5ea58e5..838457cc30 100644 --- a/metricflow-semantics/tests_metricflow_semantics/model/test_where_filter_spec.py +++ b/metricflow-semantics/tests_metricflow_semantics/model/test_where_filter_spec.py @@ -26,6 +26,7 @@ from dbt_semantic_interfaces.type_enums.date_part import DatePart from dbt_semantic_interfaces.type_enums.time_granularity import TimeGranularity from metricflow_semantics.model.linkable_element_property import LinkableElementProperty +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.model.semantics.linkable_element import ( ElementPathKey, LinkableDimension, @@ -67,6 +68,7 @@ def create_spec_lookup( call_parameter_set: CallParameterSet, resolved_spec: LinkableInstanceSpec, resolved_linkable_element_set: LinkableElementSet, + semantic_manifest_lookup: SemanticManifestLookup, ) -> FilterSpecResolutionLookUp: """Create a FilterSpecResolutionLookUp where the call_parameter_set maps to resolved_spec.""" return FilterSpecResolutionLookUp( @@ -80,7 +82,10 @@ def create_spec_lookup( where_filter_intersection=create_where_filter_intersection("Dimension('dummy__dimension')"), resolved_linkable_element_set=resolved_linkable_element_set, issue_set=MetricFlowQueryResolutionIssueSet.empty_instance(), - spec_pattern=ObjectBuilderNamingScheme().spec_pattern("Dimension('dummy__dimension')"), + spec_pattern=ObjectBuilderNamingScheme().spec_pattern( + "Dimension('dummy__dimension')", + semantic_manifest_lookup=semantic_manifest_lookup, + ), object_builder_str="Dimension('dummy__dimension')", ), ), @@ -95,6 +100,7 @@ def create_where_filter_intersection(sql_template: str) -> WhereFilterIntersecti def test_dimension_in_filter( # noqa: D103 column_association_resolver: ColumnAssociationResolver, + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: where_filter_specs = WhereSpecFactory( column_association_resolver=column_association_resolver, @@ -128,6 +134,7 @@ def test_dimension_in_filter( # noqa: D103 ) } ), + semantic_manifest_lookup=simple_semantic_manifest_lookup, ), ).create_from_where_filter_intersection( filter_location=EXAMPLE_FILTER_LOCATION, @@ -148,6 +155,7 @@ def test_dimension_in_filter( # noqa: D103 def test_dimension_in_filter_with_grain( # noqa: D103 column_association_resolver: ColumnAssociationResolver, + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: where_filter_specs = WhereSpecFactory( column_association_resolver=column_association_resolver, @@ -186,6 +194,7 @@ def test_dimension_in_filter_with_grain( # noqa: D103 ) } ), + semantic_manifest_lookup=simple_semantic_manifest_lookup, ), ).create_from_where_filter_intersection( filter_location=EXAMPLE_FILTER_LOCATION, @@ -212,6 +221,7 @@ def test_dimension_in_filter_with_grain( # noqa: D103 def test_time_dimension_in_filter( # noqa: D103 column_association_resolver: ColumnAssociationResolver, + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: where_filter_specs = WhereSpecFactory( column_association_resolver=column_association_resolver, @@ -250,6 +260,7 @@ def test_time_dimension_in_filter( # noqa: D103 ) } ), + semantic_manifest_lookup=simple_semantic_manifest_lookup, ), ).create_from_where_filter_intersection( filter_location=EXAMPLE_FILTER_LOCATION, @@ -276,6 +287,7 @@ def test_time_dimension_in_filter( # noqa: D103 def test_time_dimension_with_grain_in_name( # noqa: D103 column_association_resolver: ColumnAssociationResolver, + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: where_filter_specs = WhereSpecFactory( column_association_resolver=column_association_resolver, @@ -314,6 +326,7 @@ def test_time_dimension_with_grain_in_name( # noqa: D103 ) } ), + semantic_manifest_lookup=simple_semantic_manifest_lookup, ), ).create_from_where_filter_intersection( filter_location=EXAMPLE_FILTER_LOCATION, @@ -340,6 +353,7 @@ def test_time_dimension_with_grain_in_name( # noqa: D103 def test_date_part_in_filter( # noqa: D103 column_association_resolver: ColumnAssociationResolver, + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: where_filter_specs = WhereSpecFactory( column_association_resolver=column_association_resolver, @@ -379,6 +393,7 @@ def test_date_part_in_filter( # noqa: D103 ) } ), + semantic_manifest_lookup=simple_semantic_manifest_lookup, ), ).create_from_where_filter_intersection( filter_location=EXAMPLE_FILTER_LOCATION, @@ -405,7 +420,9 @@ def test_date_part_in_filter( # noqa: D103 @pytest.fixture(scope="session") -def resolved_spec_lookup() -> FilterSpecResolutionLookUp: +def resolved_spec_lookup( + simple_semantic_manifest_lookup: SemanticManifestLookup, +) -> FilterSpecResolutionLookUp: """A spec lookup that maps "TimeDimension('metric_time', 'week', 'year')" to the corresponding spec.""" return FilterSpecResolutionLookUp( spec_resolutions=( @@ -447,7 +464,9 @@ def resolved_spec_lookup() -> FilterSpecResolutionLookUp: ) } ), - spec_pattern=ObjectBuilderNamingScheme().spec_pattern("Dimension('dummy__dimension')"), + spec_pattern=ObjectBuilderNamingScheme().spec_pattern( + "Dimension('dummy__dimension')", semantic_manifest_lookup=simple_semantic_manifest_lookup + ), issue_set=MetricFlowQueryResolutionIssueSet.empty_instance(), object_builder_str="Dimension('dummy__dimension')", ), @@ -531,7 +550,7 @@ def test_date_part_less_than_grain_in_filter( # noqa: D103 def test_entity_in_filter( # noqa: D103 column_association_resolver: ColumnAssociationResolver, - resolved_spec_lookup: FilterSpecResolutionLookUp, + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: where_filter = PydanticWhereFilter( where_sql_template="{{ Entity('user', entity_path=['listing']) }} == 'example_user_id'" @@ -566,6 +585,7 @@ def test_entity_in_filter( # noqa: D103 ) } ), + semantic_manifest_lookup=simple_semantic_manifest_lookup, ), ).create_from_where_filter(filter_location=EXAMPLE_FILTER_LOCATION, where_filter=where_filter) @@ -580,7 +600,7 @@ def test_entity_in_filter( # noqa: D103 def test_metric_in_filter( # noqa: D103 column_association_resolver: ColumnAssociationResolver, - resolved_spec_lookup: FilterSpecResolutionLookUp, + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: where_filter = PydanticWhereFilter(where_sql_template="{{ Metric('bookings', group_by=['listing']) }} > 2") @@ -624,6 +644,7 @@ def test_metric_in_filter( # noqa: D103 ) } ), + semantic_manifest_lookup=simple_semantic_manifest_lookup, ), ).create_from_where_filter(filter_location=EXAMPLE_FILTER_LOCATION, where_filter=where_filter) @@ -636,7 +657,10 @@ def test_metric_in_filter( # noqa: D103 ) -def test_dimension_time_dimension_parity(column_association_resolver: ColumnAssociationResolver) -> None: # noqa +def test_dimension_time_dimension_parity( # noqa: D103 + column_association_resolver: ColumnAssociationResolver, + simple_semantic_manifest_lookup: SemanticManifestLookup, +) -> None: def get_spec(dimension: str) -> WhereFilterSpec: where_filter = PydanticWhereFilter(where_sql_template="{{" + dimension + "}} = '2020'") filter_location = WhereFilterLocation.for_query((MetricReference("example_metric"),)) @@ -682,7 +706,9 @@ def get_spec(dimension: str) -> WhereFilterSpec: ) } ), - spec_pattern=ObjectBuilderNamingScheme().spec_pattern("Dimension('dummy__dimension')"), + spec_pattern=ObjectBuilderNamingScheme().spec_pattern( + "Dimension('dummy__dimension')", semantic_manifest_lookup=simple_semantic_manifest_lookup + ), issue_set=MetricFlowQueryResolutionIssueSet.empty_instance(), object_builder_str="Dimension('dummy__dimension')", ), diff --git a/metricflow-semantics/tests_metricflow_semantics/naming/test_dunder_naming_scheme.py b/metricflow-semantics/tests_metricflow_semantics/naming/test_dunder_naming_scheme.py index 981476cfcc..2724ead45f 100644 --- a/metricflow-semantics/tests_metricflow_semantics/naming/test_dunder_naming_scheme.py +++ b/metricflow-semantics/tests_metricflow_semantics/naming/test_dunder_naming_scheme.py @@ -6,6 +6,7 @@ from dbt_semantic_interfaces.references import EntityReference from dbt_semantic_interfaces.type_enums import TimeGranularity from dbt_semantic_interfaces.type_enums.date_part import DatePart +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.naming.dunder_scheme import DunderNamingScheme from metricflow_semantics.specs.dimension_spec import DimensionSpec from metricflow_semantics.specs.entity_spec import EntitySpec @@ -84,9 +85,15 @@ def test_input_follows_scheme(dunder_naming_scheme: DunderNamingScheme) -> None: def test_spec_pattern( # noqa: D103 - dunder_naming_scheme: DunderNamingScheme, specs: Sequence[LinkableInstanceSpec] + dunder_naming_scheme: DunderNamingScheme, + specs: Sequence[LinkableInstanceSpec], + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: # noqa: D103 - assert tuple(dunder_naming_scheme.spec_pattern("listing__user__country").match(specs)) == ( + assert tuple( + dunder_naming_scheme.spec_pattern( + "listing__user__country", semantic_manifest_lookup=simple_semantic_manifest_lookup + ).match(specs) + ) == ( DimensionSpec( element_name="country", entity_links=( @@ -96,13 +103,21 @@ def test_spec_pattern( # noqa: D103 ), ) - assert tuple(dunder_naming_scheme.spec_pattern("metric_time").match(specs)) == ( + assert tuple( + dunder_naming_scheme.spec_pattern( + "metric_time", semantic_manifest_lookup=simple_semantic_manifest_lookup + ).match(specs) + ) == ( MTD_SPEC_WEEK, MTD_SPEC_MONTH, MTD_SPEC_YEAR, ) - assert tuple(dunder_naming_scheme.spec_pattern("booking__listing__user").match(specs)) == ( + assert tuple( + dunder_naming_scheme.spec_pattern( + "booking__listing__user", semantic_manifest_lookup=simple_semantic_manifest_lookup + ).match(specs) + ) == ( EntitySpec( element_name="user", entity_links=( @@ -112,4 +127,8 @@ def test_spec_pattern( # noqa: D103 ), ) - assert tuple(dunder_naming_scheme.spec_pattern("metric_time__month").match(specs)) == (MTD_SPEC_MONTH,) + assert tuple( + dunder_naming_scheme.spec_pattern( + "metric_time__month", semantic_manifest_lookup=simple_semantic_manifest_lookup + ).match(specs) + ) == (MTD_SPEC_MONTH,) diff --git a/metricflow-semantics/tests_metricflow_semantics/naming/test_metric_name_scheme.py b/metricflow-semantics/tests_metricflow_semantics/naming/test_metric_name_scheme.py index f4874abe24..b8e057c491 100644 --- a/metricflow-semantics/tests_metricflow_semantics/naming/test_metric_name_scheme.py +++ b/metricflow-semantics/tests_metricflow_semantics/naming/test_metric_name_scheme.py @@ -3,6 +3,7 @@ from typing import Sequence import pytest +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.naming.metric_scheme import MetricNamingScheme from metricflow_semantics.specs.dimension_spec import DimensionSpec from metricflow_semantics.specs.instance_spec import InstanceSpec @@ -15,21 +16,25 @@ def metric_naming_scheme() -> MetricNamingScheme: # noqa: D103 def test_input_str(metric_naming_scheme: MetricNamingScheme) -> None: # noqa: D103 - assert metric_naming_scheme.input_str(MetricSpec(element_name="example_metric")) == "example_metric" + assert metric_naming_scheme.input_str(MetricSpec(element_name="bookings")) == "bookings" def test_input_follows_scheme(metric_naming_scheme: MetricNamingScheme) -> None: # noqa: D103 - assert metric_naming_scheme.input_str_follows_scheme("some_metric_name") + assert metric_naming_scheme.input_str_follows_scheme("listings") -def test_spec_pattern(metric_naming_scheme: MetricNamingScheme) -> None: # noqa: D103 - spec_pattern = metric_naming_scheme.spec_pattern("metric_0") +def test_spec_pattern( # noqa: D103 + metric_naming_scheme: MetricNamingScheme, simple_semantic_manifest_lookup: SemanticManifestLookup +) -> None: + spec_pattern = metric_naming_scheme.spec_pattern( + "bookings", semantic_manifest_lookup=simple_semantic_manifest_lookup + ) specs: Sequence[InstanceSpec] = ( - MetricSpec(element_name="metric_0"), - MetricSpec(element_name="metric_1"), + MetricSpec(element_name="bookings"), + MetricSpec(element_name="listings"), # Shouldn't happen in practice, but checks to see that only metric specs are matched. - DimensionSpec(element_name="metric_0", entity_links=()), + DimensionSpec(element_name="bookings", entity_links=()), ) - assert (MetricSpec(element_name="metric_0"),) == tuple(spec_pattern.match(specs)) + assert (MetricSpec(element_name="bookings"),) == tuple(spec_pattern.match(specs)) diff --git a/metricflow-semantics/tests_metricflow_semantics/naming/test_object_builder_naming_scheme.py b/metricflow-semantics/tests_metricflow_semantics/naming/test_object_builder_naming_scheme.py index 8416c935e1..1b8a058a32 100644 --- a/metricflow-semantics/tests_metricflow_semantics/naming/test_object_builder_naming_scheme.py +++ b/metricflow-semantics/tests_metricflow_semantics/naming/test_object_builder_naming_scheme.py @@ -6,6 +6,7 @@ from dbt_semantic_interfaces.references import EntityReference from dbt_semantic_interfaces.type_enums import TimeGranularity from dbt_semantic_interfaces.type_enums.date_part import DatePart +from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.naming.object_builder_scheme import ObjectBuilderNamingScheme from metricflow_semantics.specs.dimension_spec import DimensionSpec from metricflow_semantics.specs.entity_spec import EntitySpec @@ -80,10 +81,15 @@ def test_input_follows_scheme(object_builder_naming_scheme: ObjectBuilderNamingS def test_spec_pattern( # noqa: D103 - object_builder_naming_scheme: ObjectBuilderNamingScheme, specs: Sequence[LinkableInstanceSpec] + object_builder_naming_scheme: ObjectBuilderNamingScheme, + specs: Sequence[LinkableInstanceSpec], + simple_semantic_manifest_lookup: SemanticManifestLookup, ) -> None: assert tuple( - object_builder_naming_scheme.spec_pattern("Dimension('listing__country', entity_path=['booking'])").match(specs) + object_builder_naming_scheme.spec_pattern( + "Dimension('listing__country', entity_path=['booking'])", + semantic_manifest_lookup=simple_semantic_manifest_lookup, + ).match(specs) ) == ( DimensionSpec( element_name="country", @@ -97,7 +103,8 @@ def test_spec_pattern( # noqa: D103 assert tuple( object_builder_naming_scheme.spec_pattern( "TimeDimension('listing__creation_time', time_granularity_name='month', date_part_name='day', " - "entity_path=['booking'])" + "entity_path=['booking'])", + semantic_manifest_lookup=simple_semantic_manifest_lookup, ).match(specs) ) == ( TimeDimensionSpec( @@ -108,14 +115,21 @@ def test_spec_pattern( # noqa: D103 ), ) - assert tuple(object_builder_naming_scheme.spec_pattern("TimeDimension('metric_time')").match(specs)) == ( + assert tuple( + object_builder_naming_scheme.spec_pattern( + "TimeDimension('metric_time')", semantic_manifest_lookup=simple_semantic_manifest_lookup + ).match(specs) + ) == ( MTD_SPEC_WEEK, MTD_SPEC_MONTH, MTD_SPEC_YEAR, ) assert tuple( - object_builder_naming_scheme.spec_pattern("Entity('user', entity_path=['booking', 'listing'])").match(specs) + object_builder_naming_scheme.spec_pattern( + "Entity('user', entity_path=['booking', 'listing'])", + semantic_manifest_lookup=simple_semantic_manifest_lookup, + ).match(specs) ) == ( EntitySpec( element_name="user", @@ -124,7 +138,9 @@ def test_spec_pattern( # noqa: D103 ) assert tuple( - object_builder_naming_scheme.spec_pattern("Metric('bookings', group_by=['listing'])").match(specs) + object_builder_naming_scheme.spec_pattern( + "Metric('bookings', group_by=['listing'])", semantic_manifest_lookup=simple_semantic_manifest_lookup + ).match(specs) ) == ( GroupByMetricSpec( element_name="bookings", diff --git a/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/filter_spec_resolution/test_spec_lookup.py b/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/filter_spec_resolution/test_spec_lookup.py index e77068305c..3a9b6c9215 100644 --- a/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/filter_spec_resolution/test_spec_lookup.py +++ b/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/filter_spec_resolution/test_spec_lookup.py @@ -21,7 +21,6 @@ from dbt_semantic_interfaces.transformations.transform_rule import SemanticManifestTransformRule from metricflow_semantics.mf_logging.pretty_print import mf_pformat from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup -from metricflow_semantics.naming.naming_scheme import QueryItemNamingScheme from metricflow_semantics.query.group_by_item.filter_spec_resolution.filter_pattern_factory import ( DefaultWhereFilterPatternFactory, ) @@ -58,7 +57,6 @@ def assert_spec_lookup_snapshot_equal( # noqa: D103 def test_filter_spec_resolution( # noqa: D103 request: FixtureRequest, mf_test_configuration: MetricFlowTestConfiguration, - naming_scheme: QueryItemNamingScheme, ambiguous_resolution_manifest_lookup: SemanticManifestLookup, resolution_dags: Dict[AmbiguousResolutionQueryId, GroupByItemResolutionDag], dag_case_id: str, diff --git a/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/test_matching_item_for_filters.py b/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/test_matching_item_for_filters.py index 7b15b8c1ed..9d1eca6e14 100644 --- a/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/test_matching_item_for_filters.py +++ b/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/test_matching_item_for_filters.py @@ -7,7 +7,6 @@ from _pytest.fixtures import FixtureRequest from dbt_semantic_interfaces.naming.keywords import METRIC_TIME_ELEMENT_NAME from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup -from metricflow_semantics.naming.naming_scheme import QueryItemNamingScheme from metricflow_semantics.naming.object_builder_scheme import ObjectBuilderNamingScheme from metricflow_semantics.query.group_by_item.filter_spec_resolution.filter_location import WhereFilterLocation from metricflow_semantics.query.group_by_item.group_by_item_resolver import GroupByItemResolver @@ -27,7 +26,6 @@ def test_ambiguous_metric_time_in_query_filter( # noqa: D103 request: FixtureRequest, mf_test_configuration: MetricFlowTestConfiguration, - naming_scheme: QueryItemNamingScheme, ambiguous_resolution_manifest_lookup: SemanticManifestLookup, resolution_dags: Dict[AmbiguousResolutionQueryId, GroupByItemResolutionDag], dag_case_id: str, @@ -40,7 +38,9 @@ def test_ambiguous_metric_time_in_query_filter( # noqa: D103 ) input_str = f"TimeDimension('{METRIC_TIME_ELEMENT_NAME}')" - spec_pattern = ObjectBuilderNamingScheme().spec_pattern(input_str) + spec_pattern = ObjectBuilderNamingScheme().spec_pattern( + input_str, semantic_manifest_lookup=ambiguous_resolution_manifest_lookup + ) assert isinstance(resolution_dag.sink_node, QueryGroupByItemResolutionNode) result = group_by_item_resolver.resolve_matching_item_for_filters( diff --git a/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/test_matching_item_for_querying.py b/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/test_matching_item_for_querying.py index 7b9aa5a429..e87dc3a54c 100644 --- a/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/test_matching_item_for_querying.py +++ b/metricflow-semantics/tests_metricflow_semantics/query/group_by_item/test_matching_item_for_querying.py @@ -38,7 +38,9 @@ def test_ambiguous_metric_time_in_query( # noqa: D103 resolution_dag=resolution_dag, ) - spec_pattern = ObjectBuilderNamingScheme().spec_pattern(f"TimeDimension('{METRIC_TIME_ELEMENT_NAME}')") + spec_pattern = ObjectBuilderNamingScheme().spec_pattern( + f"TimeDimension('{METRIC_TIME_ELEMENT_NAME}')", semantic_manifest_lookup=ambiguous_resolution_manifest_lookup + ) result = group_by_item_resolver.resolve_matching_item_for_querying( spec_pattern=spec_pattern, @@ -76,7 +78,10 @@ def test_unavailable_group_by_item_in_derived_metric_parent( manifest_lookup=ambiguous_resolution_manifest_lookup, resolution_dag=resolution_dag, ) - spec_pattern = naming_scheme.spec_pattern("Dimension('monthly_measure_entity__creation_time')") + spec_pattern = naming_scheme.spec_pattern( + "Dimension('monthly_measure_entity__creation_time')", + semantic_manifest_lookup=ambiguous_resolution_manifest_lookup, + ) result = group_by_item_resolver.resolve_matching_item_for_querying( spec_pattern=spec_pattern, @@ -106,7 +111,9 @@ def test_invalid_group_by_item( # noqa: D103 input_str = "Dimension('monthly_measure_entity__invalid_dimension')" result = group_by_item_resolver.resolve_matching_item_for_querying( - spec_pattern=naming_scheme.spec_pattern(input_str), + spec_pattern=naming_scheme.spec_pattern( + input_str, semantic_manifest_lookup=ambiguous_resolution_manifest_lookup + ), suggestion_generator=None, )