Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
courtneyholcomb committed Apr 15, 2024
1 parent 228e721 commit 201bbcb
Show file tree
Hide file tree
Showing 6 changed files with 636 additions and 273 deletions.
34 changes: 28 additions & 6 deletions metricflow/model/semantics/linkable_spec_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,27 @@ def _get_joined_elements(self, measure_semantic_model: SemanticModel) -> Linkabl
"""Get the elements that can be generated by joining other models to the given model."""
# Create single-hop elements
join_paths = []
linkable_metrics_set = LinkableElementSet()
for entity in measure_semantic_model.entities:
# Get LinkableMetrics. These can use fan-out join paths since they will be aggregated on the right side.
for semantic_model_for_metric_join in self._entity_to_semantic_model[entity.name]:
if semantic_model_for_metric_join.name == measure_semantic_model.name:
continue
new_linkable_metrics_set = self.get_joinable_metrics_for_semantic_model(
semantic_model=semantic_model_for_metric_join,
using_join_path=SemanticModelJoinPath(
(
SemanticModelJoinPathElement(
semantic_model_reference=semantic_model_for_metric_join.reference,
join_on_entity=entity.reference,
),
)
),
)
linkable_metrics_set = LinkableElementSet.merge_by_path_key(
[linkable_metrics_set, new_linkable_metrics_set]
)
# Get all other linkable elements. These can only use join paths that won't fan out.
semantic_models = self._get_semantic_models_joinable_to_entity(
left_semantic_model_reference=measure_semantic_model.reference,
entity_reference=entity.reference,
Expand All @@ -841,6 +861,7 @@ def _get_joined_elements(self, measure_semantic_model: SemanticModel) -> Linkabl
)
)
)

single_hop_elements = LinkableElementSet.merge_by_path_key(
[
self.create_linkable_element_set_from_join_path(
Expand All @@ -851,6 +872,8 @@ def _get_joined_elements(self, measure_semantic_model: SemanticModel) -> Linkabl
]
)

# TODO: enable more hops for linkable metrics?

# Create multi-hop elements. At each iteration, we generate the list of valid elements based on the current join
# path, extend all paths to include the next valid semantic model, then repeat.
multi_hop_elements = LinkableElementSet()
Expand Down Expand Up @@ -880,7 +903,7 @@ def _get_joined_elements(self, measure_semantic_model: SemanticModel) -> Linkabl
)
join_paths = new_join_paths

return LinkableElementSet.merge_by_path_key((single_hop_elements, multi_hop_elements))
return LinkableElementSet.merge_by_path_key((single_hop_elements, linkable_metrics_set, multi_hop_elements))

def _get_linkable_element_set_for_measure(
self,
Expand Down Expand Up @@ -1006,7 +1029,10 @@ def create_linkable_element_set_from_join_path(
join_path: SemanticModelJoinPath,
with_properties: FrozenSet[LinkableElementProperties],
) -> LinkableElementSet:
"""Given the current path, generate the respective linkable elements from the last semantic model in the path."""
"""Given the current path, generate the respective linkable elements from the last semantic model in the path.
Does not include LinkableMetrics.
"""
semantic_model = self._semantic_model_lookup.get_by_reference(join_path.last_semantic_model_reference)
assert semantic_model

Expand Down Expand Up @@ -1042,7 +1068,6 @@ def create_linkable_element_set_from_join_path(

for entity in semantic_model.entities:
# Avoid creating "booking_id__booking_id"
# TODO: we are getting the above - figure out why
if entity.reference != join_path.last_entity_link:
linkable_entities.append(
LinkableEntity(
Expand All @@ -1061,9 +1086,6 @@ def create_linkable_element_set_from_join_path(
path_key_to_linkable_entities={
linkable_entity.path_key: (linkable_entity,) for linkable_entity in linkable_entities
},
path_key_to_linkable_metrics=self.get_joinable_metrics_for_semantic_model(
semantic_model=semantic_model, using_join_path=join_path
).path_key_to_linkable_metrics,
)

def get_joinable_metrics_for_entity(self, entity_reference: EntityReference) -> Set[MetricReference]:
Expand Down
37 changes: 16 additions & 21 deletions tests/model/test_semantic_model_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,6 @@ def test_get_agg_time_dimension_specs_for_measure(semantic_model_lookup: Semanti


def test_linkable_metrics_for_measure( # noqa: D103
request: FixtureRequest,
mf_test_configuration: MetricFlowTestConfiguration,
metric_lookup: MetricLookup,
semantic_model_lookup: SemanticModelLookup,
) -> None:
Expand All @@ -218,15 +216,13 @@ def test_linkable_metrics_for_measure( # noqa: D103
).path_key_to_linkable_metrics.values()
for linkable_metric in linkable_metric_tuple
}
print("num::", len(actual_metrics))

semantic_models = semantic_model_lookup.get_semantic_models_for_measure(measure_reference)
assert len(semantic_models) == 1
measure_semantic_model = semantic_models[0]

already_seen = set()

# Check for all single-hop metrics
expected_single_hop_metrics = [
linkable_metric
for linkable_metrics in metric_lookup._linkable_spec_resolver.get_joinable_metrics_for_semantic_model(
Expand All @@ -243,24 +239,24 @@ def test_linkable_metrics_for_measure( # noqa: D103

expected_multi_hop_metrics = []
for entity in measure_semantic_model.entities:
next_semantic_models = metric_lookup._linkable_spec_resolver._get_semantic_models_joinable_to_entity(
left_semantic_model_reference=measure_semantic_model.reference, entity_reference=entity.reference
)
next_semantic_models = metric_lookup._linkable_spec_resolver._entity_to_semantic_model[entity.name]
for next_semantic_model in next_semantic_models:
expected_multi_hop_metrics += [
linkable_metric
for linkable_metrics in metric_lookup._linkable_spec_resolver.get_joinable_metrics_for_semantic_model(
semantic_model=next_semantic_model,
using_join_path=SemanticModelJoinPath(
(
SemanticModelJoinPathElement(
semantic_model_reference=next_semantic_model.reference, join_on_entity=entity.reference
),
)
if next_semantic_model.name == measure_semantic_model.name:
continue
# TODO: use SemanticModelJoinPath.from_single_element when merged
join_path = SemanticModelJoinPath(
(
SemanticModelJoinPathElement(
semantic_model_reference=next_semantic_model.reference, join_on_entity=entity.reference
),
).path_key_to_linkable_metrics.values()
for linkable_metric in linkable_metrics
]
)
)
for linkable_metrics in metric_lookup._linkable_spec_resolver.get_joinable_metrics_for_semantic_model(
semantic_model=next_semantic_model,
using_join_path=join_path,
).path_key_to_linkable_metrics.values():
for linkable_metric in linkable_metrics:
expected_multi_hop_metrics.append(linkable_metric)

for expected_metric in expected_multi_hop_metrics:
assert expected_metric in actual_metrics
Expand All @@ -269,5 +265,4 @@ def test_linkable_metrics_for_measure( # noqa: D103
actual_metrics.remove(expected_metric)

# Check that we didn't return any unexpected linkable metrics.
# LinkableMetric(element_name='booking_value_sub_instant_add_10', join_by_semantic_model=SemanticModelReference(semantic_model_name='visits_source'), entity_links=(EntityReference(element_name='user'),), properties=frozenset({<LinkableElementProperties.JOINED: 'joined'>, <LinkableElementProperties.METRIC: 'metric'>}), join_path=(SemanticModelJoinPathElement(semantic_model_reference=SemanticModelReference(semantic_model_name='visits_source'), join_on_entity=EntityReference(element_name='user')),))
assert len(actual_metrics) == 0, f"Didn't find linkable metrics: {actual_metrics}"
Loading

0 comments on commit 201bbcb

Please sign in to comment.