Skip to content

Commit

Permalink
Use MetricTimeDefaultGranularityPattern to resolve metric_time gr…
Browse files Browse the repository at this point in the history
…anularity (#1332)

Resolve metric time using new spec pattern in group by and filter nodes.
This is a change from defaulting to the minimum time granularity for
`metric_time`. Now we will use the max default granularity for the
requested metrics.

Note the snapshot updates. These reflect another behavior change in
which we don't error if `metric_time` is queried for metrics with two
different default granularities. Instead, we choose the larger of the
two, which is guaranteed to work for both metrics.
  • Loading branch information
courtneyholcomb authored Jul 16, 2024
1 parent 2296688 commit e46af9a
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 613 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20240628-074617.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Use Metric.time_granularity to resolve metric_time.
time: 2024-06-28T07:46:17.768805-07:00
custom:
Author: courtneyholcomb
Issue: "1310"
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
)
from metricflow_semantics.query.suggestion_generator import QueryItemSuggestionGenerator
from metricflow_semantics.specs.instance_spec import LinkableInstanceSpec
from metricflow_semantics.specs.patterns.metric_time_default_granularity import MetricTimeDefaultGranularityPattern
from metricflow_semantics.specs.patterns.minimum_time_grain import MinimumTimeGrainPattern
from metricflow_semantics.specs.patterns.no_group_by_metric import NoGroupByMetricPattern
from metricflow_semantics.specs.patterns.spec_pattern import SpecPattern
Expand Down Expand Up @@ -81,10 +82,10 @@ def resolve_matching_item_for_querying(
spec_pattern: SpecPattern,
suggestion_generator: Optional[QueryItemSuggestionGenerator],
) -> GroupByItemResolution:
"""Returns the spec that corresponds the one described by spec_pattern and is valid for the query.
"""Returns the spec that corresponds to the one described by spec_pattern and is valid for the query.
For queries, if the pattern matches to a spec for the same element at different grains, the spec with the finest
common grain is returned.
common grain is returned, unless the spec is metric_time, in which case the default grain is returned.
"""
push_down_visitor = _PushDownGroupByItemCandidatesVisitor(
manifest_lookup=self._manifest_lookup,
Expand All @@ -101,9 +102,13 @@ def resolve_matching_item_for_querying(
issue_set=push_down_result.issue_set,
)

push_down_result = push_down_result.filter_candidates_by_pattern(
for candidate_filter in (
# Default pattern must come first to avoid removing default grain options prematurely.
MetricTimeDefaultGranularityPattern(push_down_result.max_metric_default_time_granularity),
MinimumTimeGrainPattern(),
)
):
push_down_result = push_down_result.filter_candidates_by_pattern(candidate_filter)

logger.info(
f"Spec pattern:\n"
f"{indent(mf_pformat(spec_pattern))}\n"
Expand Down Expand Up @@ -152,12 +157,19 @@ def resolve_matching_item_for_filters(

push_down_visitor = _PushDownGroupByItemCandidatesVisitor(
manifest_lookup=self._manifest_lookup,
source_spec_patterns=(spec_pattern, MinimumTimeGrainPattern()),
source_spec_patterns=(spec_pattern,),
suggestion_generator=suggestion_generator,
)

push_down_result: PushDownResult = resolution_node.accept(push_down_visitor)

for candidate_filter in (
# Default pattern must come first to avoid removing default grain options prematurely.
MetricTimeDefaultGranularityPattern(push_down_result.max_metric_default_time_granularity),
MinimumTimeGrainPattern(),
):
push_down_result = push_down_result.filter_candidates_by_pattern(candidate_filter)

if push_down_result.candidate_set.num_candidates == 0:
return GroupByItemResolution(
spec=None,
Expand Down
17 changes: 11 additions & 6 deletions metricflow-semantics/metricflow_semantics/query/query_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ def _get_saved_query(self, saved_query_parameter: SavedQueryParameter) -> SavedQ
return matching_saved_queries[0]

@staticmethod
def _metric_time_granularity(time_dimension_specs: Sequence[TimeDimensionSpec]) -> Optional[TimeGranularity]:
def _get_smallest_requested_metric_time_granularity(
time_dimension_specs: Sequence[TimeDimensionSpec],
) -> Optional[TimeGranularity]:
matching_specs: Sequence[InstanceSpec] = time_dimension_specs

for pattern_to_apply in (
Expand All @@ -172,18 +174,21 @@ def _adjust_time_constraint(
time_dimension_specs_in_query: Sequence[TimeDimensionSpec],
time_constraint: TimeRangeConstraint,
) -> TimeRangeConstraint:
metric_time_granularity = MetricFlowQueryParser._metric_time_granularity(time_dimension_specs_in_query)
"""Change the time range so that the ends are at the ends of the requested time granularity windows.
e.g. [2020-01-15, 2020-2-15] with MONTH granularity -> [2020-01-01, 2020-02-29]
"""
metric_time_granularity = MetricFlowQueryParser._get_smallest_requested_metric_time_granularity(
time_dimension_specs_in_query
)
if metric_time_granularity is None:
# This indicates there were no metric time specs in the query, so use smallest available granularity for metric_time.
group_by_item_resolver = GroupByItemResolver(
manifest_lookup=self._manifest_lookup,
resolution_dag=resolution_dag,
)
metric_time_granularity = group_by_item_resolver.resolve_min_metric_time_grain()

"""Change the time range so that the ends are at the ends of the appropriate time granularity windows.
e.g. [2020-01-15, 2020-2-15] with MONTH granularity -> [2020-01-01, 2020-02-29]
"""
return self._time_period_adjuster.expand_time_constraint_to_fill_granularity(
time_constraint=time_constraint,
granularity=metric_time_granularity,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,108 +1,44 @@
GroupByItemResolution(
linkable_element_set=LinkableElementSet(),
issue_set=MetricFlowQueryResolutionIssueSet(
issues=(
NoCommonItemsInParents(
issue_type=ERROR,
query_resolution_path=MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_5),
MetricGroupByItemResolutionNode(node_id=mtr_10),
spec=TimeDimensionSpec(element_name='metric_time', time_granularity=YEAR),
linkable_element_set=LinkableElementSet(
path_key_to_linkable_dimensions={
ElementPathKey(
element_name='metric_time',
element_type=TIME_DIMENSION,
time_granularity=YEAR,
): (
LinkableDimension(
defined_in_semantic_model=SemanticModelReference(
semantic_model_name='monthly_measures_source',
),
),
parent_candidate_sets=(
GroupByItemCandidateSet(
linkable_element_set=LinkableElementSet(
path_key_to_linkable_dimensions={
ElementPathKey(
element_name='metric_time',
element_type=TIME_DIMENSION,
time_granularity=MONTH,
): (
LinkableDimension(
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',
),
),
properties=frozenset(
'METRIC_TIME',
),
time_granularity=MONTH,
),
),
},
),
measure_paths=(
MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_5),
MetricGroupByItemResolutionNode(node_id=mtr_10),
MetricGroupByItemResolutionNode(node_id=mtr_8),
MeasureGroupByItemSourceNode(node_id=msr_7),
),
),
),
path_from_leaf_node=MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_5),
MetricGroupByItemResolutionNode(node_id=mtr_10),
MetricGroupByItemResolutionNode(node_id=mtr_8),
),
element_name='metric_time',
dimension_type=TIME,
join_path=SemanticModelJoinPath(
left_semantic_model_reference=SemanticModelReference(
semantic_model_name='monthly_measures_source',
),
),
GroupByItemCandidateSet(
linkable_element_set=LinkableElementSet(
path_key_to_linkable_dimensions={
ElementPathKey(
element_name='metric_time',
element_type=TIME_DIMENSION,
time_granularity=YEAR,
): (
LinkableDimension(
defined_in_semantic_model=SemanticModelReference(
semantic_model_name='yearly_measure_source',
),
element_name='metric_time',
dimension_type=TIME,
join_path=SemanticModelJoinPath(
left_semantic_model_reference=SemanticModelReference(
semantic_model_name='yearly_measure_source',
),
),
properties=frozenset(
'METRIC_TIME',
),
time_granularity=YEAR,
),
),
},
),
measure_paths=(
MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_5),
MetricGroupByItemResolutionNode(node_id=mtr_10),
MetricGroupByItemResolutionNode(node_id=mtr_9),
MeasureGroupByItemSourceNode(node_id=msr_8),
),
),
),
path_from_leaf_node=MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_5),
MetricGroupByItemResolutionNode(node_id=mtr_10),
MetricGroupByItemResolutionNode(node_id=mtr_9),
),
properties=frozenset(
'DERIVED_TIME_GRANULARITY',
'METRIC_TIME',
),
time_granularity=YEAR,
),
LinkableDimension(
defined_in_semantic_model=SemanticModelReference(
semantic_model_name='yearly_measure_source',
),
element_name='metric_time',
dimension_type=TIME,
join_path=SemanticModelJoinPath(
left_semantic_model_reference=SemanticModelReference(
semantic_model_name='yearly_measure_source',
),
),
properties=frozenset('METRIC_TIME',),
time_granularity=YEAR,
),
),
),
},
),
)
Original file line number Diff line number Diff line change
@@ -1,101 +1,44 @@
GroupByItemResolution(
linkable_element_set=LinkableElementSet(),
issue_set=MetricFlowQueryResolutionIssueSet(
issues=(
NoCommonItemsInParents(
issue_type=ERROR,
query_resolution_path=MetricFlowQueryResolutionPath(
resolution_path_nodes=(QueryGroupByItemResolutionNode(node_id=qr_3),),
),
parent_candidate_sets=(
GroupByItemCandidateSet(
linkable_element_set=LinkableElementSet(
path_key_to_linkable_dimensions={
ElementPathKey(
element_name='metric_time',
element_type=TIME_DIMENSION,
time_granularity=MONTH,
): (
LinkableDimension(
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',
),
),
properties=frozenset(
'METRIC_TIME',
),
time_granularity=MONTH,
),
),
},
),
measure_paths=(
MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_3),
MetricGroupByItemResolutionNode(node_id=mtr_3),
MeasureGroupByItemSourceNode(node_id=msr_3),
),
),
),
path_from_leaf_node=MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_3),
MetricGroupByItemResolutionNode(node_id=mtr_3),
),
),
spec=TimeDimensionSpec(element_name='metric_time', time_granularity=YEAR),
linkable_element_set=LinkableElementSet(
path_key_to_linkable_dimensions={
ElementPathKey(
element_name='metric_time',
element_type=TIME_DIMENSION,
time_granularity=YEAR,
): (
LinkableDimension(
defined_in_semantic_model=SemanticModelReference(
semantic_model_name='monthly_measures_source',
),
GroupByItemCandidateSet(
linkable_element_set=LinkableElementSet(
path_key_to_linkable_dimensions={
ElementPathKey(
element_name='metric_time',
element_type=TIME_DIMENSION,
time_granularity=YEAR,
): (
LinkableDimension(
defined_in_semantic_model=SemanticModelReference(
semantic_model_name='yearly_measure_source',
),
element_name='metric_time',
dimension_type=TIME,
join_path=SemanticModelJoinPath(
left_semantic_model_reference=SemanticModelReference(
semantic_model_name='yearly_measure_source',
),
),
properties=frozenset(
'METRIC_TIME',
),
time_granularity=YEAR,
),
),
},
),
measure_paths=(
MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_3),
MetricGroupByItemResolutionNode(node_id=mtr_4),
MeasureGroupByItemSourceNode(node_id=msr_4),
),
),
element_name='metric_time',
dimension_type=TIME,
join_path=SemanticModelJoinPath(
left_semantic_model_reference=SemanticModelReference(
semantic_model_name='monthly_measures_source',
),
path_from_leaf_node=MetricFlowQueryResolutionPath(
resolution_path_nodes=(
QueryGroupByItemResolutionNode(node_id=qr_3),
MetricGroupByItemResolutionNode(node_id=mtr_4),
),
),
properties=frozenset(
'DERIVED_TIME_GRANULARITY',
'METRIC_TIME',
),
time_granularity=YEAR,
),
LinkableDimension(
defined_in_semantic_model=SemanticModelReference(
semantic_model_name='yearly_measure_source',
),
element_name='metric_time',
dimension_type=TIME,
join_path=SemanticModelJoinPath(
left_semantic_model_reference=SemanticModelReference(
semantic_model_name='yearly_measure_source',
),
),
properties=frozenset('METRIC_TIME',),
time_granularity=YEAR,
),
),
),
},
),
)
Loading

0 comments on commit e46af9a

Please sign in to comment.