diff --git a/metricflow-semantics/metricflow_semantics/query/group_by_item/candidate_push_down/push_down_visitor.py b/metricflow-semantics/metricflow_semantics/query/group_by_item/candidate_push_down/push_down_visitor.py index c44c78b52d..0e37559b90 100644 --- a/metricflow-semantics/metricflow_semantics/query/group_by_item/candidate_push_down/push_down_visitor.py +++ b/metricflow-semantics/metricflow_semantics/query/group_by_item/candidate_push_down/push_down_visitor.py @@ -6,6 +6,7 @@ from typing import Dict, FrozenSet, Iterator, List, Optional, Sequence, Tuple from dbt_semantic_interfaces.enum_extension import assert_values_exhausted +from dbt_semantic_interfaces.references import MetricReference from dbt_semantic_interfaces.type_enums import MetricType from dbt_semantic_interfaces.type_enums.time_granularity import TimeGranularity from typing_extensions import override @@ -15,6 +16,10 @@ from metricflow_semantics.model.linkable_element_property import LinkableElementProperty from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.query.group_by_item.candidate_push_down.group_by_item_candidate import GroupByItemCandidateSet +from metricflow_semantics.query.group_by_item.filter_spec_resolution.filter_location import ( + WhereFilterLocation, + WhereFilterLocationType, +) from metricflow_semantics.query.group_by_item.resolution_dag.resolution_nodes.base_node import ( GroupByItemResolutionNode, GroupByItemResolutionNodeVisitor, @@ -135,6 +140,7 @@ def __init__( source_spec_patterns: Sequence[SpecPattern] = (), with_any_property: Optional[FrozenSet[LinkableElementProperty]] = None, without_any_property: Optional[FrozenSet[LinkableElementProperty]] = None, + filter_location: Optional[WhereFilterLocation] = None, ) -> None: """Initializer. @@ -146,6 +152,7 @@ def __init__( with_any_property: Only consider group-by-items with these properties from the measure nodes. without_any_property: Only consider group-by-items without any of these properties (see LinkableElementProperty). + filter_location: If resolving a where filter item, where this filter was defined. """ self._semantic_manifest_lookup = manifest_lookup self._source_spec_patterns = tuple(source_spec_patterns) @@ -153,6 +160,7 @@ def __init__( self._with_any_property = with_any_property self._without_any_property = without_any_property self._suggestion_generator = suggestion_generator + self._filter_location = filter_location @override def visit_measure_node(self, node: MeasureGroupByItemSourceNode) -> PushDownResult: @@ -369,11 +377,24 @@ def visit_metric_node(self, node: MetricGroupByItemResolutionNode) -> PushDownRe ) ) + metric_to_use_for_time_granularity_resolution = metric + # If this is resolving a filter defined on an input metric, use the outer metric's time_granularity. + if ( + node.metric_input_location + and self._filter_location + and self._filter_location.location_type == WhereFilterLocationType.INPUT_METRIC + ): + metric_to_use_for_time_granularity_resolution = self._semantic_manifest_lookup.metric_lookup.get_metric( + node.metric_input_location.derived_metric_reference + ) + # If time granularity is not set for the metric, defaults to DAY if available, else the smallest available granularity. # Note: ignores any granularity set on input metrics. - metric_default_time_granularity = metric.time_granularity or max( + metric_default_time_granularity = metric_to_use_for_time_granularity_resolution.time_granularity or max( TimeGranularity.DAY, - self._semantic_manifest_lookup.metric_lookup.get_min_queryable_time_granularity(node.metric_reference), + self._semantic_manifest_lookup.metric_lookup.get_min_queryable_time_granularity( + MetricReference(metric_to_use_for_time_granularity_resolution.name) + ), ) if matched_items.spec_count == 0: diff --git a/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_location.py b/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_location.py index 8741ae1fc5..2f8e25e345 100644 --- a/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_location.py +++ b/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_location.py @@ -1,23 +1,34 @@ from __future__ import annotations from dataclasses import dataclass +from enum import Enum from typing import Sequence, Tuple from dbt_semantic_interfaces.references import MetricReference from more_itertools import is_sorted +class WhereFilterLocationType(Enum): + """Describes the location where the filter is defined.""" + + QUERY = "query" + METRIC = "metric" + INPUT_METRIC = "input_metric" + + @dataclass(frozen=True) class WhereFilterLocation: """Describe the location of a where filter. When describing a query, the metric references are the metrics in the query. - When describing a metric, the metric reference is just the metric. + When describing a metric, the metric reference just the metric where the filter is defined. + When describing an input metric, the metric references are the outer metric and the input metric. Since this is used to resolve valid group-by items for a where filter, this is sufficient as the valid group-by items depend only on the metrics queried. """ + location_type: WhereFilterLocationType # These should be sorted for consistency in comparisons. metric_references: Tuple[MetricReference, ...] @@ -26,8 +37,16 @@ def __post_init__(self) -> None: # noqa: D105 @staticmethod def for_query(metric_references: Sequence[MetricReference]) -> WhereFilterLocation: # noqa: D102 - return WhereFilterLocation(metric_references=tuple(sorted(metric_references))) + return WhereFilterLocation( + metric_references=tuple(sorted(metric_references)), location_type=WhereFilterLocationType.QUERY + ) @staticmethod def for_metric(metric_reference: MetricReference) -> WhereFilterLocation: # noqa: D102 - return WhereFilterLocation(metric_references=(metric_reference,)) + return WhereFilterLocation(metric_references=(metric_reference,), location_type=WhereFilterLocationType.METRIC) + + @staticmethod + def for_input_metric(input_metric_reference: MetricReference) -> WhereFilterLocation: # noqa: D102 + return WhereFilterLocation( + metric_references=(input_metric_reference,), location_type=WhereFilterLocationType.INPUT_METRIC + ) diff --git a/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_spec_lookup.py b/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_spec_lookup.py index 926e3cb0e9..d1db393642 100644 --- a/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_spec_lookup.py +++ b/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_spec_lookup.py @@ -11,7 +11,6 @@ TimeDimensionCallParameterSet, ) from dbt_semantic_interfaces.protocols import WhereFilterIntersection -from dbt_semantic_interfaces.references import MetricReference from typing_extensions import override from metricflow_semantics.collection_helpers.merger import Mergeable @@ -163,28 +162,6 @@ def from_parameters( # noqa: D102 call_parameter_set=call_parameter_set, ) - @staticmethod - def for_metric_filter( - metric_reference: MetricReference, call_parameter_set: CallParameterSet - ) -> ResolvedSpecLookUpKey: - """Create a key related to a filter in a metric definition.""" - return ResolvedSpecLookUpKey( - filter_location=WhereFilterLocation.for_metric( - metric_reference, - ), - call_parameter_set=call_parameter_set, - ) - - @staticmethod - def for_query_filter( - metrics_in_query: Sequence[MetricReference], call_parameter_set: CallParameterSet - ) -> ResolvedSpecLookUpKey: - """Create a key related to a filter for a query.""" - return ResolvedSpecLookUpKey( - filter_location=WhereFilterLocation.for_query(metrics_in_query), - call_parameter_set=call_parameter_set, - ) - @dataclass(frozen=True) class FilterSpecResolution: diff --git a/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_spec_resolver.py b/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_spec_resolver.py index 0c3efcb5cc..091fddc84a 100644 --- a/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_spec_resolver.py +++ b/metricflow-semantics/metricflow_semantics/query/group_by_item/filter_spec_resolution/filter_spec_resolver.py @@ -2,11 +2,12 @@ import itertools import logging -from typing import List, Sequence +from collections import defaultdict +from typing import Dict, List, Sequence, Set from dbt_semantic_interfaces.call_parameter_sets import FilterCallParameterSets, MetricCallParameterSet from dbt_semantic_interfaces.implementations.filters.where_filter import PydanticWhereFilterIntersection -from dbt_semantic_interfaces.protocols import WhereFilter, WhereFilterIntersection +from dbt_semantic_interfaces.protocols import WhereFilter from dbt_semantic_interfaces.references import EntityReference from typing_extensions import override @@ -14,7 +15,9 @@ from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup from metricflow_semantics.naming.object_builder_str import ObjectBuilderNameConverter from metricflow_semantics.query.group_by_item.candidate_push_down.push_down_visitor import DagTraversalPathTracker -from metricflow_semantics.query.group_by_item.filter_spec_resolution.filter_location import WhereFilterLocation +from metricflow_semantics.query.group_by_item.filter_spec_resolution.filter_location import ( + WhereFilterLocation, +) from metricflow_semantics.query.group_by_item.filter_spec_resolution.filter_pattern_factory import ( WhereFilterPatternFactory, ) @@ -235,8 +238,7 @@ def visit_metric_node(self, node: MetricGroupByItemResolutionNode) -> FilterSpec self._resolve_specs_for_where_filters( current_node=node, resolution_path=resolution_path, - filter_location=WhereFilterLocation.for_metric(node.metric_reference), - where_filter_intersection=self._get_where_filters_at_metric_node(node), + where_filters_and_locations=self._get_where_filters_at_metric_node(node), ) ) @@ -255,8 +257,11 @@ def visit_query_node(self, node: QueryGroupByItemResolutionNode) -> FilterSpecRe self._resolve_specs_for_where_filters( current_node=node, resolution_path=resolution_path, - filter_location=WhereFilterLocation.for_query(node.metrics_in_query), - where_filter_intersection=node.where_filter_intersection, + where_filters_and_locations={ + WhereFilterLocation.for_query(node.metrics_in_query): set( + node.where_filter_intersection.where_filters + ) + }, ) ) @@ -268,38 +273,44 @@ def visit_no_metrics_query_node(self, node: NoMetricsGroupByItemSourceNode) -> F def _get_where_filters_at_metric_node( self, metric_node: MetricGroupByItemResolutionNode - ) -> WhereFilterIntersection: + ) -> Dict[WhereFilterLocation, Set[WhereFilter]]: """Return the filters used with a metric in the manifest. A derived metric definition can have a filter for an input metric, and we'll need to resolve that when the dataflow plan is built. """ - where_filters: List[WhereFilter] = [] + where_filters_and_locations: Dict[WhereFilterLocation, Set[WhereFilter]] = defaultdict(set) metric = self._manifest_lookup.metric_lookup.get_metric(metric_node.metric_reference) + metric_filter_location = WhereFilterLocation.for_metric(metric_reference=metric_node.metric_reference) if metric.input_measures is not None: for input_measure in metric.input_measures: if input_measure.filter is not None: - where_filters.extend(input_measure.filter.where_filters) + for input_measure_where_filter in input_measure.filter.where_filters: + where_filters_and_locations[metric_filter_location].add(input_measure_where_filter) if metric.filter is not None: - where_filters.extend(metric.filter.where_filters) + for metric_where_filter in metric.filter.where_filters: + where_filters_and_locations[metric_filter_location].add(metric_where_filter) # This is a metric that is an input metric for a derived metric. The derived metric is the child node of this # metric node. if metric_node.metric_input_location is not None: child_metric_input = metric_node.metric_input_location.get_metric_input(self._manifest_lookup.metric_lookup) if child_metric_input.filter is not None: - where_filters.extend(child_metric_input.filter.where_filters) + input_metric_filter_location = WhereFilterLocation.for_input_metric( + input_metric_reference=metric_node.metric_reference + ) + for input_metric_where_filter in child_metric_input.filter.where_filters: + where_filters_and_locations[input_metric_filter_location].add(input_metric_where_filter) - return PydanticWhereFilterIntersection(where_filters=where_filters) + return where_filters_and_locations def _resolve_specs_for_where_filters( self, current_node: ResolutionDagSinkNode, resolution_path: MetricFlowQueryResolutionPath, - filter_location: WhereFilterLocation, - where_filter_intersection: WhereFilterIntersection, + where_filters_and_locations: Dict[WhereFilterLocation, Set[WhereFilter]], ) -> FilterSpecResolutionLookUp: """Given the filters at a particular node, build the spec lookup for those filters. @@ -313,62 +324,81 @@ def _resolve_specs_for_where_filters( resolution_dag=resolution_dag, ) non_parsable_resolutions: List[NonParsableFilterResolution] = [] - filter_call_parameter_sets_to_merge: List[FilterCallParameterSets] = [] - - for where_filter in where_filter_intersection.where_filters: - try: - filter_call_parameter_sets = where_filter.call_parameter_sets - except Exception as e: - non_parsable_resolutions.append( - NonParsableFilterResolution( - filter_location_path=resolution_path, - where_filter_intersection=where_filter_intersection, - issue_set=MetricFlowQueryResolutionIssueSet.from_issue( - WhereFilterParsingIssue.from_parameters( - where_filter=where_filter, - parse_exception=e, - query_resolution_path=resolution_path, - ) - ), + filter_call_parameter_sets_by_location: Dict[WhereFilterLocation, List[FilterCallParameterSets]] = defaultdict( + list + ) + # No input metric in locations when we get here + for location, where_filters in where_filters_and_locations.items(): + for where_filter in where_filters: + try: + filter_call_parameter_sets = where_filter.call_parameter_sets + except Exception as e: + non_parsable_resolutions.append( + NonParsableFilterResolution( + filter_location_path=resolution_path, + where_filter_intersection=PydanticWhereFilterIntersection( + where_filters=list( + { + where_filter + for where_filter_set in where_filters_and_locations.values() + for where_filter in where_filter_set + } + ) + ), + issue_set=MetricFlowQueryResolutionIssueSet.from_issue( + WhereFilterParsingIssue.from_parameters( + where_filter=where_filter, + parse_exception=e, + query_resolution_path=resolution_path, + ) + ), + ) ) - ) - continue - filter_call_parameter_sets_to_merge.append(filter_call_parameter_sets) + continue + filter_call_parameter_sets_by_location[location].append(filter_call_parameter_sets) - deduped_filter_call_parameter_sets = _ResolveWhereFilterSpecVisitor._dedupe_filter_call_parameter_sets( - filter_call_parameter_sets_to_merge - ) + deduped_filter_call_parameter_sets_by_location = { + location: _ResolveWhereFilterSpecVisitor._dedupe_filter_call_parameter_sets(filter_call_parameter_sets_list) + for location, filter_call_parameter_sets_list in filter_call_parameter_sets_by_location.items() + } resolutions: List[FilterSpecResolution] = [] - for group_by_item_in_where_filter in self._map_filter_parameter_sets_to_pattern( - filter_call_parameter_sets=deduped_filter_call_parameter_sets, - ): - group_by_item_resolution = group_by_item_resolver.resolve_matching_item_for_filters( - input_str=group_by_item_in_where_filter.object_builder_str, - spec_pattern=group_by_item_in_where_filter.spec_pattern, - resolution_node=current_node, - ) - # The paths in the issue set are generated relative to the current node. For error messaging, it seems more - # helpful for those paths to be relative to the query. To do, we have to add nodes from the resolution path. - # e.g. if the current node is B, and the resolution path is [A, B], an issue might have the relative path - # [B, C]. To join those paths to produce [A, B, C], the path prefix should be [A]. - path_prefix = MetricFlowQueryResolutionPath( - resolution_path_nodes=resolution_path.resolution_path_nodes[:-1] - ) - resolutions.append( - FilterSpecResolution( - lookup_key=ResolvedSpecLookUpKey( - filter_location=filter_location, - call_parameter_set=group_by_item_in_where_filter.call_parameter_set, - ), - filter_location_path=resolution_path, - resolved_linkable_element_set=group_by_item_resolution.linkable_element_set, - where_filter_intersection=where_filter_intersection, + for ( + filter_location, + deduped_filter_call_parameter_sets, + ) in deduped_filter_call_parameter_sets_by_location.items(): + for group_by_item_in_where_filter in self._map_filter_parameter_sets_to_pattern( + filter_call_parameter_sets=deduped_filter_call_parameter_sets, + ): + group_by_item_resolution = group_by_item_resolver.resolve_matching_item_for_filters( + input_str=group_by_item_in_where_filter.object_builder_str, spec_pattern=group_by_item_in_where_filter.spec_pattern, - issue_set=group_by_item_resolution.issue_set.with_path_prefix(path_prefix), - object_builder_str=group_by_item_in_where_filter.object_builder_str, + resolution_node=current_node, + filter_location=filter_location, + ) + # The paths in the issue set are generated relative to the current node. For error messaging, it seems more + # helpful for those paths to be relative to the query. To do, we have to add nodes from the resolution path. + # e.g. if the current node is B, and the resolution path is [A, B], an issue might have the relative path + # [B, C]. To join those paths to produce [A, B, C], the path prefix should be [A]. + path_prefix = MetricFlowQueryResolutionPath( + resolution_path_nodes=resolution_path.resolution_path_nodes[:-1] + ) + resolutions.append( + FilterSpecResolution( + lookup_key=ResolvedSpecLookUpKey( + filter_location=filter_location, + call_parameter_set=group_by_item_in_where_filter.call_parameter_set, + ), + filter_location_path=resolution_path, + resolved_linkable_element_set=group_by_item_resolution.linkable_element_set, + where_filter_intersection=PydanticWhereFilterIntersection( + where_filters=list(where_filters_and_locations[filter_location]) + ), + spec_pattern=group_by_item_in_where_filter.spec_pattern, + issue_set=group_by_item_resolution.issue_set.with_path_prefix(path_prefix), + object_builder_str=group_by_item_in_where_filter.object_builder_str, + ) ) - ) if any(resolution.issue_set.has_errors for resolution in resolutions) or len(non_parsable_resolutions) > 0: return FilterSpecResolutionLookUp( diff --git a/metricflow-semantics/metricflow_semantics/query/group_by_item/group_by_item_resolver.py b/metricflow-semantics/metricflow_semantics/query/group_by_item/group_by_item_resolver.py index 401c4dae9b..542c3d6976 100644 --- a/metricflow-semantics/metricflow_semantics/query/group_by_item/group_by_item_resolver.py +++ b/metricflow-semantics/metricflow_semantics/query/group_by_item/group_by_item_resolver.py @@ -20,6 +20,7 @@ PushDownResult, _PushDownGroupByItemCandidatesVisitor, ) +from metricflow_semantics.query.group_by_item.filter_spec_resolution.filter_location import WhereFilterLocation from metricflow_semantics.query.group_by_item.resolution_dag.dag import GroupByItemResolutionDag, ResolutionDagSinkNode from metricflow_semantics.query.group_by_item.resolution_path import MetricFlowQueryResolutionPath from metricflow_semantics.query.issues.group_by_item_resolver.ambiguous_group_by_item import AmbiguousGroupByItemIssue @@ -140,6 +141,7 @@ def resolve_matching_item_for_filters( input_str: str, spec_pattern: SpecPattern, resolution_node: ResolutionDagSinkNode, + filter_location: WhereFilterLocation, ) -> GroupByItemResolution: """Returns the spec that matches the spec_pattern associated with the filter in the given node. @@ -159,6 +161,7 @@ def resolve_matching_item_for_filters( manifest_lookup=self._manifest_lookup, source_spec_patterns=(spec_pattern,), suggestion_generator=suggestion_generator, + filter_location=filter_location, ) push_down_result: PushDownResult = resolution_node.accept(push_down_visitor) diff --git a/metricflow-semantics/metricflow_semantics/test_helpers/semantic_manifest_yamls/ambiguous_resolution_manifest/metrics.yaml b/metricflow-semantics/metricflow_semantics/test_helpers/semantic_manifest_yamls/ambiguous_resolution_manifest/metrics.yaml index 53021a962c..5f7dff21e7 100644 --- a/metricflow-semantics/metricflow_semantics/test_helpers/semantic_manifest_yamls/ambiguous_resolution_manifest/metrics.yaml +++ b/metricflow-semantics/metricflow_semantics/test_helpers/semantic_manifest_yamls/ambiguous_resolution_manifest/metrics.yaml @@ -120,3 +120,37 @@ metric: metrics: - name: simple_metric_with_default_time_granularity - name: monthly_metric_0 +--- +metric: + name: simple_metric_with_default_time_granularity_and_metric_time_filter + description: Simple metric with time granularity and metric_time filter + type: simple + type_params: + measure: monthly_measure_1 + time_granularity: quarter + filter: | + {{ TimeDimension('metric_time') }} > '2020-01-01' +--- +metric: + name: derived_metric_with_time_granularity_and_outer_metric_time_filter + description: Derived metric with time granularity + type: derived + time_granularity: year + type_params: + expr: simple_metric_with_default_time_granularity * 2 + metrics: + - name: simple_metric_with_default_time_granularity + filter: | + {{ TimeDimension('metric_time') }} > '2020-01-01' +--- +metric: + name: derived_metric_with_time_granularity_and_inner_metric_time_filter + description: Derived metric with time granularity + type: derived + time_granularity: year + type_params: + expr: simple_metric_with_default_time_granularity_and_metric_time_filter * 2 + metrics: + - name: simple_metric_with_default_time_granularity_and_metric_time_filter + filter: | + {{ TimeDimension('metric_time') }} = '2020-01-01' diff --git a/metricflow-semantics/metricflow_semantics/test_helpers/semantic_manifest_yamls/simple_manifest/metrics.yaml b/metricflow-semantics/metricflow_semantics/test_helpers/semantic_manifest_yamls/simple_manifest/metrics.yaml index 6d9116f015..aa3545865d 100644 --- a/metricflow-semantics/metricflow_semantics/test_helpers/semantic_manifest_yamls/simple_manifest/metrics.yaml +++ b/metricflow-semantics/metricflow_semantics/test_helpers/semantic_manifest_yamls/simple_manifest/metrics.yaml @@ -783,3 +783,22 @@ metric: name: listings filter: "{{ Metric('views', ['listing']) }} > 10" time_granularity: week +--- +metric: + name: bookings_before_dec_20_2019 + description: Bookings up to the end of 2022 + type: simple + type_params: + measure: bookings + filter: "{{ TimeDimension('metric_time') }} < '2012-12-20'" +--- +metric: + name: bookings_between_dec_18_2019_and_dec_20_2019 + description: Bookings starting in 2020. Used to test a metric with different types of ambiguous filters in on its input metric. + type: derived + type_params: + expr: bookings_before_dec_20_2019 + metrics: + - name: bookings_before_dec_20_2019 + filter: "{{ TimeDimension('metric_time') }} > '2019-12-18'" + time_granularity: week 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 76b5f61169..7b15b8c1ed 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 @@ -9,8 +9,12 @@ 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 from metricflow_semantics.query.group_by_item.resolution_dag.dag import GroupByItemResolutionDag +from metricflow_semantics.query.group_by_item.resolution_dag.resolution_nodes.query_resolution_node import ( + QueryGroupByItemResolutionNode, +) from metricflow_semantics.test_helpers.config_helpers import MetricFlowTestConfiguration from metricflow_semantics.test_helpers.snapshot_helpers import assert_object_snapshot_equal @@ -38,10 +42,12 @@ 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) + assert isinstance(resolution_dag.sink_node, QueryGroupByItemResolutionNode) result = group_by_item_resolver.resolve_matching_item_for_filters( input_str=input_str, spec_pattern=spec_pattern, resolution_node=resolution_dag.sink_node, + filter_location=WhereFilterLocation.for_query(resolution_dag.sink_node.metrics_in_query), ) assert_object_snapshot_equal( diff --git a/metricflow-semantics/tests_metricflow_semantics/query/test_metric_time_granularity.py b/metricflow-semantics/tests_metricflow_semantics/query/test_metric_time_granularity.py index a634b635ea..4fbaba6cc0 100644 --- a/metricflow-semantics/tests_metricflow_semantics/query/test_metric_time_granularity.py +++ b/metricflow-semantics/tests_metricflow_semantics/query/test_metric_time_granularity.py @@ -114,3 +114,61 @@ def test_non_metric_time_ignores_default_granularity( # noqa: D103 obj_id="result_0", obj=query_spec, ) + + +def test_simple_metric_with_defined_metric_time_filter( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + ambiguous_resolution_query_parser: MetricFlowQueryParser, +) -> None: + """Tests that a simple metric's metric_time filter defined in its YAML uses metric's default granularity.""" + query_spec = ambiguous_resolution_query_parser.parse_and_validate_query( + metric_names=["simple_metric_with_default_time_granularity_and_metric_time_filter"] + ).query_spec + + assert_object_snapshot_equal( + request=request, + mf_test_configuration=mf_test_configuration, + obj_id="result_0", + obj=query_spec, + ) + + +def test_derived_metric_with_defined_metric_time_filter( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + ambiguous_resolution_query_parser: MetricFlowQueryParser, +) -> None: + """Tests that a derived metric's metric_time filter defined in its YAML uses outer metric's default granularity.""" + query_spec = ambiguous_resolution_query_parser.parse_and_validate_query( + metric_names=["derived_metric_with_time_granularity_and_outer_metric_time_filter"] + ).query_spec + + assert_object_snapshot_equal( + request=request, + mf_test_configuration=mf_test_configuration, + obj_id="result_0", + obj=query_spec, + ) + + +def test_derived_metric_with_defined_metric_time_filter_on_input_metric( + request: FixtureRequest, + mf_test_configuration: MetricFlowTestConfiguration, + ambiguous_resolution_query_parser: MetricFlowQueryParser, +) -> None: + """Tests a derived metric with a metric_time filter on its input metric. + + Should use the outer metric's default granularity. + Should always use the default granularity for the object where the filter is defined. + """ + query_spec = ambiguous_resolution_query_parser.parse_and_validate_query( + metric_names=["derived_metric_with_time_granularity_and_inner_metric_time_filter"] + ).query_spec + + assert_object_snapshot_equal( + request=request, + mf_test_configuration=mf_test_configuration, + obj_id="result_0", + obj=query_spec, + ) diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_linkable_spec_resolver.py/list/test_linkable_element_set_as_spec_set__set0.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_linkable_spec_resolver.py/list/test_linkable_element_set_as_spec_set__set0.txt index 042892a632..2f48fe86a4 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_linkable_spec_resolver.py/list/test_linkable_element_set_as_spec_set__set0.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_linkable_spec_resolver.py/list/test_linkable_element_set_as_spec_set__set0.txt @@ -20,6 +20,8 @@ 'listing__booking__listing__booking_value_sub_instant', 'listing__booking__listing__booking_value_sub_instant_add_10', 'listing__booking__listing__bookings', + 'listing__booking__listing__bookings_before_dec_20_2019', + 'listing__booking__listing__bookings_between_dec_18_2019_and_dec_20_2019', 'listing__booking__listing__bookings_fill_nulls_with_0', 'listing__booking__listing__bookings_fill_nulls_with_0_without_time_spine', 'listing__booking__listing__bookings_join_to_time_spine', @@ -54,6 +56,8 @@ 'listing__booking_value_sub_instant', 'listing__booking_value_sub_instant_add_10', 'listing__bookings', + 'listing__bookings_before_dec_20_2019', + 'listing__bookings_between_dec_18_2019_and_dec_20_2019', 'listing__bookings_fill_nulls_with_0', 'listing__bookings_fill_nulls_with_0_without_time_spine', 'listing__bookings_join_to_time_spine', @@ -262,6 +266,8 @@ 'user__listing__user__booking_value_sub_instant', 'user__listing__user__booking_value_sub_instant_add_10', 'user__listing__user__bookings', + 'user__listing__user__bookings_before_dec_20_2019', + 'user__listing__user__bookings_between_dec_18_2019_and_dec_20_2019', 'user__listing__user__bookings_fill_nulls_with_0', 'user__listing__user__bookings_fill_nulls_with_0_without_time_spine', 'user__listing__user__bookings_join_to_time_spine', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_derived_metric_with_defined_metric_time_filter__result_0.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_derived_metric_with_defined_metric_time_filter__result_0.txt new file mode 100644 index 0000000000..b6d9c3f3ca --- /dev/null +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_derived_metric_with_defined_metric_time_filter__result_0.txt @@ -0,0 +1,77 @@ +MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name='derived_metric_with_time_granularity_and_outer_metric_time_filter'),), + filter_intersection=PydanticWhereFilterIntersection(), + filter_spec_resolution_lookup=FilterSpecResolutionLookUp( + spec_resolutions=( + FilterSpecResolution( + lookup_key=ResolvedSpecLookUpKey( + filter_location=WhereFilterLocation( + location_type=METRIC, + metric_references=( + MetricReference( + element_name='derived_metric_with_time_granularity_and_outer_metric_time_filter', + ), + ), + ), + call_parameter_set=TimeDimensionCallParameterSet( + time_dimension_reference=TimeDimensionReference( + element_name='metric_time', + ), + ), + ), + where_filter_intersection=PydanticWhereFilterIntersection( + where_filters=[ + PydanticWhereFilter( + where_sql_template="{{ TimeDimension('metric_time') }} > '2020-01-01'\n", + ), + ], + ), + resolved_linkable_element_set=LinkableElementSet( + path_key_to_linkable_dimensions={ + ElementPathKey( + element_name='metric_time', + element_type=TIME_DIMENSION, + time_granularity=YEAR, + ): ( + LinkableDimension( + properties=( + DERIVED_TIME_GRANULARITY, + METRIC_TIME, + ), + defined_in_semantic_model=SemanticModelReference( + semantic_model_name='monthly_measures_source', + ), + element_name='metric_time', + dimension_type=TIME, + join_path=SemanticModelJoinPath( + left_semantic_model_reference=SemanticModelReference( + semantic_model_name='monthly_measures_source', + ), + ), + time_granularity=YEAR, + ), + ), + }, + ), + spec_pattern=TimeDimensionPattern( + parameter_set=EntityLinkPatternParameterSet( + fields_to_compare=( + DATE_PART, + ELEMENT_NAME, + ENTITY_LINKS, + ), + element_name='metric_time', + ), + ), + filter_location_path=MetricFlowQueryResolutionPath( + resolution_path_nodes=( + QueryGroupByItemResolutionNode(node_id=qr_1), + MetricGroupByItemResolutionNode(node_id=mtr_1), + ), + ), + object_builder_str="TimeDimension('metric_time')", + ), + ), + ), + min_max_only=False, +) diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_derived_metric_with_defined_metric_time_filter_on_input_metric__result_0.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_derived_metric_with_defined_metric_time_filter_on_input_metric__result_0.txt index 906879b627..c39e7639ff 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_derived_metric_with_defined_metric_time_filter_on_input_metric__result_0.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_derived_metric_with_defined_metric_time_filter_on_input_metric__result_0.txt @@ -34,6 +34,10 @@ MetricFlowQuerySpec( time_granularity=QUARTER, ): ( LinkableDimension( + properties=( + DERIVED_TIME_GRANULARITY, + METRIC_TIME, + ), defined_in_semantic_model=SemanticModelReference( semantic_model_name='monthly_measures_source', ), @@ -44,10 +48,6 @@ MetricFlowQuerySpec( semantic_model_name='monthly_measures_source', ), ), - properties=frozenset( - 'DERIVED_TIME_GRANULARITY', - 'METRIC_TIME', - ), time_granularity=QUARTER, ), ), @@ -103,6 +103,10 @@ MetricFlowQuerySpec( time_granularity=YEAR, ): ( LinkableDimension( + properties=( + DERIVED_TIME_GRANULARITY, + METRIC_TIME, + ), defined_in_semantic_model=SemanticModelReference( semantic_model_name='monthly_measures_source', ), @@ -113,10 +117,6 @@ MetricFlowQuerySpec( semantic_model_name='monthly_measures_source', ), ), - properties=frozenset( - 'DERIVED_TIME_GRANULARITY', - 'METRIC_TIME', - ), time_granularity=YEAR, ), ), diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_simple_metric_with_defined_metric_time_filter__result_0.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_simple_metric_with_defined_metric_time_filter__result_0.txt new file mode 100644 index 0000000000..957321aff2 --- /dev/null +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_metric_time_granularity.py/MetricFlowQuerySpec/test_simple_metric_with_defined_metric_time_filter__result_0.txt @@ -0,0 +1,77 @@ +MetricFlowQuerySpec( + metric_specs=(MetricSpec(element_name='simple_metric_with_default_time_granularity_and_metric_time_filter'),), + filter_intersection=PydanticWhereFilterIntersection(), + filter_spec_resolution_lookup=FilterSpecResolutionLookUp( + spec_resolutions=( + FilterSpecResolution( + lookup_key=ResolvedSpecLookUpKey( + filter_location=WhereFilterLocation( + location_type=METRIC, + metric_references=( + MetricReference( + element_name='simple_metric_with_default_time_granularity_and_metric_time_filter', + ), + ), + ), + call_parameter_set=TimeDimensionCallParameterSet( + time_dimension_reference=TimeDimensionReference( + element_name='metric_time', + ), + ), + ), + where_filter_intersection=PydanticWhereFilterIntersection( + where_filters=[ + PydanticWhereFilter( + where_sql_template="{{ TimeDimension('metric_time') }} > '2020-01-01'\n", + ), + ], + ), + resolved_linkable_element_set=LinkableElementSet( + path_key_to_linkable_dimensions={ + ElementPathKey( + element_name='metric_time', + element_type=TIME_DIMENSION, + time_granularity=QUARTER, + ): ( + LinkableDimension( + properties=( + DERIVED_TIME_GRANULARITY, + METRIC_TIME, + ), + defined_in_semantic_model=SemanticModelReference( + semantic_model_name='monthly_measures_source', + ), + element_name='metric_time', + dimension_type=TIME, + join_path=SemanticModelJoinPath( + left_semantic_model_reference=SemanticModelReference( + semantic_model_name='monthly_measures_source', + ), + ), + time_granularity=QUARTER, + ), + ), + }, + ), + spec_pattern=TimeDimensionPattern( + parameter_set=EntityLinkPatternParameterSet( + fields_to_compare=( + DATE_PART, + ELEMENT_NAME, + ENTITY_LINKS, + ), + element_name='metric_time', + ), + ), + filter_location_path=MetricFlowQueryResolutionPath( + resolution_path_nodes=( + QueryGroupByItemResolutionNode(node_id=qr_1), + MetricGroupByItemResolutionNode(node_id=mtr_0), + ), + ), + object_builder_str="TimeDimension('metric_time')", + ), + ), + ), + min_max_only=False, +) diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_query_parser.py/ParseQueryResult/test_parse_and_validate_where_constraint_dims__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_query_parser.py/ParseQueryResult/test_parse_and_validate_where_constraint_dims__result.txt index fd2b78fa5b..bd0765d8e1 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_query_parser.py/ParseQueryResult/test_parse_and_validate_where_constraint_dims__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_query_parser.py/ParseQueryResult/test_parse_and_validate_where_constraint_dims__result.txt @@ -18,6 +18,7 @@ ParseQueryResult( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=QUERY, metric_references=( MetricReference( element_name='bookings', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_semantic_model_container.py/str/test_linkable_elements_for_measure__result0.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_semantic_model_container.py/str/test_linkable_elements_for_measure__result0.txt index e9c4f19333..bda0664dfa 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_semantic_model_container.py/str/test_linkable_elements_for_measure__result0.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_semantic_model_container.py/str/test_linkable_elements_for_measure__result0.txt @@ -36,6 +36,8 @@ Model Join-Path Entity Links ('listings_latest',) ("('listing',)", "('booking', 'listing')") booking_value_sub_instant ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('booking', 'listing')") booking_value_sub_instant_add_10 ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('booking', 'listing')") bookings ['JOINED', 'METRIC'] +('listings_latest',) ("('listing',)", "('booking', 'listing')") bookings_before_dec_20_2019 ['JOINED', 'METRIC'] +('listings_latest',) ("('listing',)", "('booking', 'listing')") bookings_between_dec_18_2019_and_dec_20_2019 ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('booking', 'listing')") bookings_fill_nulls_with_0 ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('booking', 'listing')") bookings_fill_nulls_with_0_without_time_spine ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('booking', 'listing')") bookings_join_to_time_spine ['JOINED', 'METRIC'] @@ -77,6 +79,8 @@ Model Join-Path Entity Links ('listings_latest',) ("('listing',)", "('listing',)") booking_value_sub_instant ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('listing',)") booking_value_sub_instant_add_10 ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('listing',)") bookings ['JOINED', 'METRIC'] +('listings_latest',) ("('listing',)", "('listing',)") bookings_before_dec_20_2019 ['JOINED', 'METRIC'] +('listings_latest',) ("('listing',)", "('listing',)") bookings_between_dec_18_2019_and_dec_20_2019 ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('listing',)") bookings_fill_nulls_with_0 ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('listing',)") bookings_fill_nulls_with_0_without_time_spine ['JOINED', 'METRIC'] ('listings_latest',) ("('listing',)", "('listing',)") bookings_join_to_time_spine ['JOINED', 'METRIC'] @@ -136,6 +140,8 @@ Model Join-Path Entity Links ('listings_latest',) ("('user',)", "('listing', 'user')") booking_value_sub_instant ['JOINED', 'METRIC'] ('listings_latest',) ("('user',)", "('listing', 'user')") booking_value_sub_instant_add_10 ['JOINED', 'METRIC'] ('listings_latest',) ("('user',)", "('listing', 'user')") bookings ['JOINED', 'METRIC'] +('listings_latest',) ("('user',)", "('listing', 'user')") bookings_before_dec_20_2019 ['JOINED', 'METRIC'] +('listings_latest',) ("('user',)", "('listing', 'user')") bookings_between_dec_18_2019_and_dec_20_2019 ['JOINED', 'METRIC'] ('listings_latest',) ("('user',)", "('listing', 'user')") bookings_fill_nulls_with_0 ['JOINED', 'METRIC'] ('listings_latest',) ("('user',)", "('listing', 'user')") bookings_fill_nulls_with_0_without_time_spine ['JOINED', 'METRIC'] ('listings_latest',) ("('user',)", "('listing', 'user')") bookings_join_to_time_spine ['JOINED', 'METRIC'] diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_semantic_model_container.py/tuple/test_linkable_elements_for_no_metrics_query__result0.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_semantic_model_container.py/tuple/test_linkable_elements_for_no_metrics_query__result0.txt index 949b0180f2..7cf20ab18e 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_semantic_model_container.py/tuple/test_linkable_elements_for_no_metrics_query__result0.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_semantic_model_container.py/tuple/test_linkable_elements_for_no_metrics_query__result0.txt @@ -59,6 +59,8 @@ 'company__listing__user__company__booking_value_sub_instant', 'company__listing__user__company__booking_value_sub_instant_add_10', 'company__listing__user__company__bookings', + 'company__listing__user__company__bookings_before_dec_20_2019', + 'company__listing__user__company__bookings_between_dec_18_2019_and_dec_20_2019', 'company__listing__user__company__bookings_fill_nulls_with_0', 'company__listing__user__company__bookings_fill_nulls_with_0_without_time_spine', 'company__listing__user__company__bookings_join_to_time_spine', @@ -125,6 +127,8 @@ 'guest__booking__guest__booking_value_sub_instant', 'guest__booking__guest__booking_value_sub_instant_add_10', 'guest__booking__guest__bookings', + 'guest__booking__guest__bookings_before_dec_20_2019', + 'guest__booking__guest__bookings_between_dec_18_2019_and_dec_20_2019', 'guest__booking__guest__bookings_fill_nulls_with_0', 'guest__booking__guest__bookings_fill_nulls_with_0_without_time_spine', 'guest__booking__guest__bookings_join_to_time_spine', @@ -158,6 +162,8 @@ 'guest__booking_value_sub_instant', 'guest__booking_value_sub_instant_add_10', 'guest__bookings', + 'guest__bookings_before_dec_20_2019', + 'guest__bookings_between_dec_18_2019_and_dec_20_2019', 'guest__bookings_fill_nulls_with_0', 'guest__bookings_fill_nulls_with_0_without_time_spine', 'guest__bookings_join_to_time_spine', @@ -202,6 +208,8 @@ 'host__booking__host__booking_value_sub_instant', 'host__booking__host__booking_value_sub_instant_add_10', 'host__booking__host__bookings', + 'host__booking__host__bookings_before_dec_20_2019', + 'host__booking__host__bookings_between_dec_18_2019_and_dec_20_2019', 'host__booking__host__bookings_fill_nulls_with_0', 'host__booking__host__bookings_fill_nulls_with_0_without_time_spine', 'host__booking__host__bookings_join_to_time_spine', @@ -235,6 +243,8 @@ 'host__booking_value_sub_instant', 'host__booking_value_sub_instant_add_10', 'host__bookings', + 'host__bookings_before_dec_20_2019', + 'host__bookings_between_dec_18_2019_and_dec_20_2019', 'host__bookings_fill_nulls_with_0', 'host__bookings_fill_nulls_with_0_without_time_spine', 'host__bookings_join_to_time_spine', @@ -280,6 +290,8 @@ 'listing__booking__listing__booking_value_sub_instant', 'listing__booking__listing__booking_value_sub_instant_add_10', 'listing__booking__listing__bookings', + 'listing__booking__listing__bookings_before_dec_20_2019', + 'listing__booking__listing__bookings_between_dec_18_2019_and_dec_20_2019', 'listing__booking__listing__bookings_fill_nulls_with_0', 'listing__booking__listing__bookings_fill_nulls_with_0_without_time_spine', 'listing__booking__listing__bookings_join_to_time_spine', @@ -314,6 +326,8 @@ 'listing__booking_value_sub_instant', 'listing__booking_value_sub_instant_add_10', 'listing__bookings', + 'listing__bookings_before_dec_20_2019', + 'listing__bookings_between_dec_18_2019_and_dec_20_2019', 'listing__bookings_fill_nulls_with_0', 'listing__bookings_fill_nulls_with_0_without_time_spine', 'listing__bookings_join_to_time_spine', @@ -385,6 +399,8 @@ 'lux_listing__listing__lux_listing__booking_value_sub_instant', 'lux_listing__listing__lux_listing__booking_value_sub_instant_add_10', 'lux_listing__listing__lux_listing__bookings', + 'lux_listing__listing__lux_listing__bookings_before_dec_20_2019', + 'lux_listing__listing__lux_listing__bookings_between_dec_18_2019_and_dec_20_2019', 'lux_listing__listing__lux_listing__bookings_fill_nulls_with_0', 'lux_listing__listing__lux_listing__bookings_fill_nulls_with_0_without_time_spine', 'lux_listing__listing__lux_listing__bookings_join_to_time_spine', @@ -496,6 +512,8 @@ 'user__listing__user__booking_value_sub_instant', 'user__listing__user__booking_value_sub_instant_add_10', 'user__listing__user__bookings', + 'user__listing__user__bookings_before_dec_20_2019', + 'user__listing__user__bookings_between_dec_18_2019_and_dec_20_2019', 'user__listing__user__bookings_fill_nulls_with_0', 'user__listing__user__bookings_fill_nulls_with_0_without_time_spine', 'user__listing__user__bookings_join_to_time_spine', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_derived_metrics_with_common_filtered_metric__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_derived_metrics_with_common_filtered_metric__result.txt index d7a7c80b37..fe217b1e52 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_derived_metrics_with_common_filtered_metric__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_derived_metrics_with_common_filtered_metric__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=INPUT_METRIC, metric_references=( MetricReference( element_name='monthly_metric_0', @@ -66,6 +67,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=INPUT_METRIC, metric_references=( MetricReference( element_name='monthly_metric_0', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_invalid_metric_filter__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_invalid_metric_filter__result.txt index 8f6610729e..680894f346 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_invalid_metric_filter__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_invalid_metric_filter__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=METRIC, metric_references=( MetricReference( element_name='derived_metric_with_different_parent_time_grains', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_invalid_metric_input_filter__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_invalid_metric_input_filter__result.txt index afb915c0fa..1828fec269 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_invalid_metric_input_filter__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_invalid_metric_input_filter__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=INPUT_METRIC, metric_references=( MetricReference( element_name='derived_metric_with_different_parent_time_grains', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_valid_metric_filter__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_valid_metric_filter__result.txt index 88091fbb61..c2ffe2a0f9 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_valid_metric_filter__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_valid_metric_filter__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=METRIC, metric_references=( MetricReference( element_name='derived_metric_with_same_parent_time_grains', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_valid_metric_input_filter__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_valid_metric_input_filter__result.txt index dbb7165ae2..7977c5d6e5 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_valid_metric_input_filter__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_resolution_for_valid_metric_input_filter__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=INPUT_METRIC, metric_references=( MetricReference( element_name='derived_metric_with_same_parent_time_grains', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__accumulate_last_2_months_metric__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__accumulate_last_2_months_metric__result.txt index 0c24cd6674..d2edee4213 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__accumulate_last_2_months_metric__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__accumulate_last_2_months_metric__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=QUERY, metric_references=( MetricReference( element_name='accumulate_last_2_months_metric', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__derived_metric_with_different_parent_time_grains__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__derived_metric_with_different_parent_time_grains__result.txt index 4f7d466824..c08cfc993e 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__derived_metric_with_different_parent_time_grains__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__derived_metric_with_different_parent_time_grains__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=QUERY, metric_references=( MetricReference( element_name='derived_metric_with_different_parent_time_grains', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__derived_metric_with_same_parent_time_grains__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__derived_metric_with_same_parent_time_grains__result.txt index 19e5bd1ede..e6d1c789dd 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__derived_metric_with_same_parent_time_grains__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__derived_metric_with_same_parent_time_grains__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=QUERY, metric_references=( MetricReference( element_name='derived_metric_with_same_parent_time_grains', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__metrics_with_different_time_grains__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__metrics_with_different_time_grains__result.txt index d6d07856bb..7687fd07bf 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__metrics_with_different_time_grains__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__metrics_with_different_time_grains__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=QUERY, metric_references=( MetricReference( element_name='monthly_metric_0', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__metrics_with_same_time_grains__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__metrics_with_same_time_grains__result.txt index 9ded545300..32df7d86ae 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__metrics_with_same_time_grains__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__metrics_with_same_time_grains__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=QUERY, metric_references=( MetricReference( element_name='monthly_metric_0', diff --git a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__simple_metric__result.txt b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__simple_metric__result.txt index a64ddd65bd..686f6fe4a4 100644 --- a/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__simple_metric__result.txt +++ b/metricflow-semantics/tests_metricflow_semantics/snapshots/test_spec_lookup.py/str/test_filter_spec_resolution__simple_metric__result.txt @@ -3,6 +3,7 @@ FilterSpecResolutionLookUp( FilterSpecResolution( lookup_key=ResolvedSpecLookUpKey( filter_location=WhereFilterLocation( + location_type=QUERY, metric_references=( MetricReference( element_name='monthly_metric_0', diff --git a/metricflow/dataflow/builder/dataflow_plan_builder.py b/metricflow/dataflow/builder/dataflow_plan_builder.py index b27c09511d..1090c2c999 100644 --- a/metricflow/dataflow/builder/dataflow_plan_builder.py +++ b/metricflow/dataflow/builder/dataflow_plan_builder.py @@ -1284,7 +1284,7 @@ def _build_input_metric_specs_for_derived_metric( for input_metric in metric.input_metrics: filter_specs = filter_spec_factory.create_from_where_filter_intersection( - filter_location=WhereFilterLocation.for_metric(input_metric.as_reference), + filter_location=WhereFilterLocation.for_input_metric(input_metric_reference=input_metric.as_reference), filter_intersection=input_metric.filter, ) diff --git a/tests_metricflow/integration/test_cases/itest_metrics.yaml b/tests_metricflow/integration/test_cases/itest_metrics.yaml index 52b805f9ea..7c6e96f8f6 100644 --- a/tests_metricflow/integration/test_cases/itest_metrics.yaml +++ b/tests_metricflow/integration/test_cases/itest_metrics.yaml @@ -2215,3 +2215,15 @@ integration_test: GROUP BY {{ render_date_trunc("ds", TimeGranularity.DAY) }} ) a ON b.ds = a.ds WHERE {{ render_between_time_constraint("b.ds", "2020-01-03", "2020-01-03") }} +--- +integration_test: + name: metric_time_filter_on_input_metric + description: Test a metric with a filter defined on its input metric. + model: SIMPLE_MODEL + metrics: ["bookings_between_dec_18_2019_and_dec_20_2019"] + check_query: | + SELECT + SUM(1) AS bookings_between_dec_18_2019_and_dec_20_2019 + FROM {{ source_schema }}.fct_bookings + WHERE ({{ render_date_trunc("ds", TimeGranularity.DAY) }} < '2012-12-20') + AND ({{ render_date_trunc("ds", TimeGranularity.WEEK) }} > '2019-12-18')