Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests for Metric.time_granularity #1325

Merged
merged 8 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,31 @@ metric:
metrics:
- name: monthly_metric_0
filter: "{{ TimeDimension('metric_time') }} = '2020-01-01'"
---
metric:
name: simple_metric_with_time_granularity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More like ...with_default_time_granularity?

description: Simple metric with time granularity
type: simple
type_params:
measure: monthly_measure_1
time_granularity: quarter
---
metric:
name: derived_metric_with_time_granularity
description: Derived metric with time granularity
type: derived
time_granularity: year
type_params:
expr: simple_metric_with_time_granularity * 2
metrics:
- name: simple_metric_with_time_granularity
---
metric:
name: derived_metric_without_time_granularity
description: Derived metric without time granularity
type: derived
type_params:
expr: simple_metric_with_time_granularity * monthly_metric_0
metrics:
- name: simple_metric_with_time_granularity
- name: monthly_metric_0
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ metric:
type: simple
type_params:
measure: largest_listing
time_granularity: month
---
metric:
name: "identity_verifications"
Expand Down Expand Up @@ -626,6 +627,7 @@ metric:
name: bookings
join_to_timespine: true
fill_nulls_with: 0
time_granularity: week
---
metric:
name: every_two_days_bookers_fill_nulls_with_0
Expand Down Expand Up @@ -780,3 +782,4 @@ metric:
denominator:
name: listings
filter: "{{ Metric('views', ['listing']) }} > 10"
time_granularity: week
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from __future__ import annotations

import pytest
from _pytest.fixtures import FixtureRequest
from metricflow_semantics.model.semantic_manifest_lookup import SemanticManifestLookup
from metricflow_semantics.query.query_parser import MetricFlowQueryParser
from metricflow_semantics.test_helpers.config_helpers import MetricFlowTestConfiguration
from metricflow_semantics.test_helpers.snapshot_helpers import assert_object_snapshot_equal


@pytest.fixture
def query_parser( # noqa: D103
simple_semantic_manifest_lookup: SemanticManifestLookup,
) -> MetricFlowQueryParser:
return MetricFlowQueryParser(semantic_manifest_lookup=simple_semantic_manifest_lookup)


@pytest.fixture
def ambiguous_resolution_query_parser( # noqa: D103
ambiguous_resolution_manifest_lookup: SemanticManifestLookup,
) -> MetricFlowQueryParser:
return MetricFlowQueryParser(semantic_manifest_lookup=ambiguous_resolution_manifest_lookup)


def test_simple_metric_with_explicit_time_granularity( # noqa: D103
request: FixtureRequest,
mf_test_configuration: MetricFlowTestConfiguration,
query_parser: MetricFlowQueryParser,
) -> None:
query_spec = query_parser.parse_and_validate_query(
metric_names=["largest_listing"], group_by_names=["metric_time"]
).query_spec

assert_object_snapshot_equal(
request=request,
mf_test_configuration=mf_test_configuration,
obj_id="result_0",
obj=query_spec,
)


def test_simple_metric_without_explicit_time_granularity(
request: FixtureRequest,
mf_test_configuration: MetricFlowTestConfiguration,
ambiguous_resolution_query_parser: MetricFlowQueryParser,
) -> None:
"""Tests that a metric without default granularity uses the min granualrity for its agg_time_dim."""
query_spec = ambiguous_resolution_query_parser.parse_and_validate_query(
metric_names=["monthly_metric_0"],
group_by_names=["metric_time"],
).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_explicit_time_granularity(
request: FixtureRequest,
mf_test_configuration: MetricFlowTestConfiguration,
ambiguous_resolution_query_parser: MetricFlowQueryParser,
) -> None:
"""Tests that a derived metric with default granularity ignores the default granularities set on its input metrics."""
query_spec = ambiguous_resolution_query_parser.parse_and_validate_query(
metric_names=["derived_metric_with_time_granularity"],
group_by_names=["metric_time"],
).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_without_explicit_time_granularity(
request: FixtureRequest,
mf_test_configuration: MetricFlowTestConfiguration,
ambiguous_resolution_query_parser: MetricFlowQueryParser,
) -> None:
"""Tests a derived metric without explicit default granularity.

Should ignore the default granularities set on its input metrics.
"""
query_spec = ambiguous_resolution_query_parser.parse_and_validate_query(
metric_names=["derived_metric_without_time_granularity"],
group_by_names=["metric_time"],
).query_spec

assert_object_snapshot_equal(
request=request,
mf_test_configuration=mf_test_configuration,
obj_id="result_0",
obj=query_spec,
)


def test_non_metric_time_ignores_default_granularity( # noqa: D103
request: FixtureRequest,
mf_test_configuration: MetricFlowTestConfiguration,
query_parser: MetricFlowQueryParser,
) -> None:
query_spec = query_parser.parse_and_validate_query(
metric_names=["largest_listing"], group_by_names=["listing__ds"]
).query_spec

assert_object_snapshot_equal(
request=request,
mf_test_configuration=mf_test_configuration,
obj_id="result_0",
obj=query_spec,
)
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
expr: "1"
agg: sum
create_metric: true
- name: monthly_bookings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed now?

expr: "1"
agg: sum
agg_time_dimension: ds_month

dimensions:
- name: is_instant
Expand All @@ -59,13 +63,43 @@
type: time
type_params:
time_granularity: day
- name: ds_month
type: time
type_params:
time_granularity: month

primary_entity: booking

entities:
- name: listing
type: foreign
expr: listing_id

---
metric:
name: instant_bookings
description: instant bookings
type: simple
type_params:
measure: bookings
filter: "{{ Dimension('booking__is_instant') }}"
---
metric:
name: month_bookings
description: monthly bookings
type: simple
type_params:
measure: monthly_bookings
---
metric:
name: instant_plus_months_bookings
description: instant plus month bookings
type: derived
type_params:
expr: instant_bookings + month_bookings
metrics:
- name: instant_bookings
- name: month_bookings
"""
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name='derived_metric_with_time_granularity'),),
time_dimension_specs=(TimeDimensionSpec(element_name='metric_time', time_granularity=YEAR),),
filter_intersection=PydanticWhereFilterIntersection(),
filter_spec_resolution_lookup=FilterSpecResolutionLookUp(),
min_max_only=False,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name='derived_metric_without_time_granularity'),),
time_dimension_specs=(TimeDimensionSpec(element_name='metric_time', time_granularity=MONTH),),
filter_intersection=PydanticWhereFilterIntersection(),
filter_spec_resolution_lookup=FilterSpecResolutionLookUp(),
min_max_only=False,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name='largest_listing'),),
time_dimension_specs=(
TimeDimensionSpec(
element_name='ds',
entity_links=(EntityReference(element_name='listing'),),
time_granularity=DAY,
),
),
filter_intersection=PydanticWhereFilterIntersection(),
filter_spec_resolution_lookup=FilterSpecResolutionLookUp(),
min_max_only=False,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name='largest_listing'),),
time_dimension_specs=(TimeDimensionSpec(element_name='metric_time', time_granularity=MONTH),),
filter_intersection=PydanticWhereFilterIntersection(),
filter_spec_resolution_lookup=FilterSpecResolutionLookUp(),
min_max_only=False,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name='monthly_metric_0'),),
time_dimension_specs=(TimeDimensionSpec(element_name='metric_time', time_granularity=MONTH),),
filter_intersection=PydanticWhereFilterIntersection(),
filter_spec_resolution_lookup=FilterSpecResolutionLookUp(),
min_max_only=False,
)
1 change: 0 additions & 1 deletion metricflow/dataflow/builder/dataflow_plan_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,6 @@ def _build_cumulative_metric_output_node(
predicate_pushdown_state: PredicatePushdownState,
for_group_by_source_node: bool = False,
) -> DataflowPlanNode:
# TODO: replace with default_grain once added to YAML spec
default_granularity = self._metric_lookup.get_min_queryable_time_granularity(metric_spec.reference)

queried_agg_time_dimensions = queried_linkable_specs.included_agg_time_dimension_specs_for_metric(
Expand Down
3 changes: 1 addition & 2 deletions metricflow/plan_conversion/dataflow_to_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,7 @@ def visit_join_over_time_range_node(self, node: JoinOverTimeRangeNode) -> SqlDat
input_data_set_alias = self._next_unique_table_alias()

# Find requested agg_time_dimensions in parent instance set.
# For now, will use instance with smallest granularity in time spine join.
# TODO: use metric's default_grain once that property is available.
# Will use instance with smallest granularity in time spine join.
agg_time_dimension_instance_for_join: Optional[TimeDimensionInstance] = None
requested_agg_time_dimension_instances: Tuple[TimeDimensionInstance, ...] = ()
for instance in input_data_set.instance_set.time_dimension_instances:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ integration_test:
---
integration_test:
name: daily_metric_with_monthly_time_dimension
description: Query a metric with a month-granularity time dimensions.
description: Query a metric with a month-granularity time dimensions. Filter should expand to requested granularity.
model: EXTENDED_DATE_MODEL
metrics: ["bookings"]
group_bys: ["metric_time__month"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ integration_test:
check_query: |
SELECT
MAX(capacity) as largest_listing
, created_at AS metric_time__day
, {{ render_date_trunc("created_at", TimeGranularity.MONTH) }} AS metric_time__month
FROM {{ source_schema }}.dim_listings_latest
GROUP BY
2
Expand Down
31 changes: 18 additions & 13 deletions tests_metricflow/integration/test_cases/itest_metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1143,17 +1143,22 @@ integration_test:
group_by_objs: [{"name": "metric_time"}]
check_query: |
SELECT
subq_5.ds AS metric_time__day
subq_4.metric_time__week
, COALESCE(subq_3.bookings, 0) AS bookings_fill_nulls_with_0
FROM {{ source_schema }}.mf_time_spine subq_5
FROM (
SELECT
{{ render_date_trunc("ds", TimeGranularity.WEEK) }} AS metric_time__week
FROM {{ source_schema }}.mf_time_spine subq_5
GROUP BY {{ render_date_trunc("ds", TimeGranularity.WEEK) }}
) subq_4
LEFT OUTER JOIN (
SELECT
{{ render_date_trunc("ds", TimeGranularity.DAY) }} AS metric_time__day
{{ render_date_trunc("ds", TimeGranularity.WEEK) }} AS metric_time__week
, SUM(1) AS bookings
FROM {{ source_schema }}.fct_bookings bookings_source_src_1
GROUP BY 1
FROM {{ source_schema }}.fct_bookings
GROUP BY {{ render_date_trunc("ds", TimeGranularity.WEEK) }}
) subq_3
ON subq_5.ds = subq_3.metric_time__day
ON subq_4.metric_time__week = subq_3.metric_time__week
---
integration_test:
name: simple_fill_nulls_with_0_month
Expand Down Expand Up @@ -1343,32 +1348,32 @@ integration_test:
group_by_objs: [{"name": "metric_time"}]
check_query: |
SELECT
COALESCE(subq_7.metric_time__day, subq_12.metric_time__day) AS metric_time__day
COALESCE(subq_7.metric_time__week, subq_12.metric_time__week) AS metric_time__week
, COALESCE(MAX(subq_7.bookings_fill_nulls_with_0), 0) AS bookings_fill_nulls_with_0
, MAX(subq_12.views) AS views
FROM (
SELECT
subq_5.ds AS metric_time__day
{{ render_date_trunc("subq_5.ds", TimeGranularity.WEEK) }} AS metric_time__week
, COALESCE(subq_3.bookings, 0) AS bookings_fill_nulls_with_0
FROM {{ source_schema }}.mf_time_spine subq_5
LEFT OUTER JOIN (
SELECT
{{ render_date_trunc("ds", TimeGranularity.DAY) }} AS metric_time__day
{{ render_date_trunc("ds", TimeGranularity.WEEK) }} AS metric_time__week
, SUM(1) AS bookings
FROM {{ source_schema }}.fct_bookings bookings_source_src_1
GROUP BY 1
) subq_3
ON subq_5.ds = subq_3.metric_time__day
ON {{ render_date_trunc("subq_5.ds", TimeGranularity.WEEK) }} = subq_3.metric_time__week
) subq_7
FULL OUTER JOIN (
SELECT
{{ render_date_trunc("ds", TimeGranularity.DAY) }} AS metric_time__day
{{ render_date_trunc("ds", TimeGranularity.WEEK) }} AS metric_time__week
, SUM(1) AS views
FROM {{ source_schema }}.fct_views views_source_src_9
GROUP BY 1
) subq_12
ON subq_7.metric_time__day = subq_12.metric_time__day
GROUP BY COALESCE(subq_7.metric_time__day, subq_12.metric_time__day)
ON subq_7.metric_time__week = subq_12.metric_time__week
GROUP BY COALESCE(subq_7.metric_time__week, subq_12.metric_time__week)
---
integration_test:
name: fill_nulls_with_0_multi_metric_query_with_nesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def test_simple_join_to_time_spine_with_queried_filter(
)


@pytest.mark.skip("Time constraints don't use the group by resolution DAG yet. Need to determine expected behavior.")
@pytest.mark.sql_engine_snapshot
def test_join_to_time_spine_with_time_constraint(
request: FixtureRequest,
Expand Down
Loading
Loading