Skip to content

Commit

Permalink
Merge pull request #856 from dbt-labs/expand-cumulative-and-derived-m…
Browse files Browse the repository at this point in the history
…etric-tests

Expand test coverage for cumulative and derived offset metrics
  • Loading branch information
tlento authored Nov 8, 2023
2 parents dc076a1 + 5e6ea1f commit b812e7a
Show file tree
Hide file tree
Showing 39 changed files with 7,297 additions and 757 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,34 @@ integration_test:
ON b.created_at <= a.ds
GROUP BY a.ds
ORDER by a.ds
---
integration_test:
name: cumulative_metric_with_non_adjustable_filter
description: Query a cumulative metric that has a time filter which cannot be adapted to cover the full input range
model: SIMPLE_MODEL
metrics: ["every_two_days_bookers"]
group_bys: ["metric_time__day"]
order_bys: ["metric_time__day"]
where_filter: |
{{ render_time_dimension_template('metric_time', 'day') }} = '2019-12-20'
or {{ render_time_dimension_template('metric_time', 'day') }} = '2020-01-04'
check_query: |
SELECT
COUNT (DISTINCT(b.guest_id)) as every_two_days_bookers
, a.ds AS metric_time__day
FROM (
SELECT ds
FROM {{ mf_time_spine_source }}
WHERE {{ render_time_constraint("ds", "2019-12-19", "2020-01-04") }}
) a
INNER JOIN (
SELECT
guest_id
, ds
FROM {{ source_schema }}.fct_bookings
) b
ON b.ds <= a.ds AND b.ds > {{ render_date_sub("a", "ds", 2, TimeGranularity.DAY) }}
WHERE {{ render_time_constraint("a.ds", "2019-12-20", "2019-12-20") }}
OR {{ render_time_constraint("a.ds", "2020-01-04", "2020-01-04") }}
GROUP BY a.ds
ORDER BY a.ds
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@

import pytest
from _pytest.fixtures import FixtureRequest
from dbt_semantic_interfaces.implementations.filters.where_filter import PydanticWhereFilter
from dbt_semantic_interfaces.test_utils import as_datetime
from dbt_semantic_interfaces.type_enums.time_granularity import TimeGranularity

from metricflow.dataflow.builder.dataflow_plan_builder import DataflowPlanBuilder
from metricflow.dataset.dataset import DataSet
from metricflow.filters.time_constraint import TimeRangeConstraint
from metricflow.plan_conversion.dataflow_to_sql import DataflowToSqlQueryPlanConverter
from metricflow.protocols.sql_client import SqlClient
from metricflow.specs.column_assoc import ColumnAssociationResolver
from metricflow.specs.specs import (
MetricFlowQuerySpec,
MetricSpec,
TimeDimensionSpec,
)
from metricflow.specs.where_filter_transform import WhereSpecFactory
from metricflow.test.fixtures.model_fixtures import ConsistentIdObjectRepository
from metricflow.test.fixtures.setup_fixtures import MetricFlowTestSessionState
from metricflow.test.query_rendering.compare_rendered_query import convert_and_check
Expand All @@ -30,7 +34,7 @@ def test_cumulative_metric(
consistent_id_object_repository: ConsistentIdObjectRepository,
sql_client: SqlClient,
) -> None:
"""Tests converting a dataflow plan to a SQL query plan where there is a cumulative metric to compute."""
"""Tests rendering a basic cumulative metric query."""
dataflow_plan = dataflow_plan_builder.build_plan(
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name="trailing_2_months_revenue"),),
Expand Down Expand Up @@ -63,14 +67,19 @@ def test_cumulative_metric_with_time_constraint(
consistent_id_object_repository: ConsistentIdObjectRepository,
sql_client: SqlClient,
) -> None:
"""Tests converting a dataflow plan to a SQL query plan where there is a cumulative metric to compute."""
"""Tests rendering a cumulative metric query with an adjustable time constraint.
Not all query inputs with time constraint filters allow us to adjust the time constraint to include the full
span of input data for a cumulative metric, but when we receive a time constraint filter expression we can
automatically adjust it should render a query similar to this one.
"""
dataflow_plan = dataflow_plan_builder.build_plan(
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name="trailing_2_months_revenue"),),
dimension_specs=(),
time_dimension_specs=(
TimeDimensionSpec(
element_name="ds",
element_name="metric_time",
entity_links=(),
time_granularity=TimeGranularity.MONTH,
),
Expand All @@ -90,6 +99,48 @@ def test_cumulative_metric_with_time_constraint(
)


@pytest.mark.sql_engine_snapshot
def test_cumulative_metric_with_non_adjustable_time_filter(
request: FixtureRequest,
mf_test_session_state: MetricFlowTestSessionState,
column_association_resolver: ColumnAssociationResolver,
dataflow_plan_builder: DataflowPlanBuilder,
dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter,
consistent_id_object_repository: ConsistentIdObjectRepository,
sql_client: SqlClient,
) -> None:
"""Tests rendering a cumulative metric query with a time filter that cannot be automatically adjusted.
Not all query inputs with time constraint filters allow us to adjust the time constraint to include the full
span of input data for a cumulative metric. When we do not have an adjustable time filter we must include all
input data in order to ensure the cumulative metric is correct.
"""
dataflow_plan = dataflow_plan_builder.build_plan(
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name="every_two_days_bookers"),),
time_dimension_specs=(DataSet.metric_time_dimension_spec(time_granularity=TimeGranularity.DAY),),
where_constraint=WhereSpecFactory(
column_association_resolver=column_association_resolver,
).create_from_where_filter(
PydanticWhereFilter(
where_sql_template=(
"{{ TimeDimension('metric_time', 'day') }} = '2020-01-03' "
"or {{ TimeDimension('metric_time', 'day') }} = '2020-01-07'"
)
),
),
),
)

convert_and_check(
request=request,
mf_test_session_state=mf_test_session_state,
dataflow_to_sql_converter=dataflow_to_sql_converter,
sql_client=sql_client,
node=dataflow_plan.sink_output_nodes[0].parent_node,
)


@pytest.mark.sql_engine_snapshot
def test_cumulative_metric_no_ds(
request: FixtureRequest,
Expand All @@ -99,7 +150,7 @@ def test_cumulative_metric_no_ds(
consistent_id_object_repository: ConsistentIdObjectRepository,
sql_client: SqlClient,
) -> None:
"""Tests converting a dataflow plan to a SQL query plan where there is a cumulative metric to compute."""
"""Tests rendering a cumulative metric with no time dimension specified."""
dataflow_plan = dataflow_plan_builder.build_plan(
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name="trailing_2_months_revenue"),),
Expand All @@ -126,7 +177,7 @@ def test_cumulative_metric_no_window(
consistent_id_object_repository: ConsistentIdObjectRepository,
sql_client: SqlClient,
) -> None:
"""Tests converting a dataflow plan to a SQL query plan where there is a windowless cumulative metric to compute."""
"""Tests rendering a query where there is a windowless cumulative metric to compute."""
dataflow_plan = dataflow_plan_builder.build_plan(
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name="revenue_all_time"),),
Expand Down Expand Up @@ -159,7 +210,7 @@ def test_cumulative_metric_no_window_with_time_constraint(
consistent_id_object_repository: ConsistentIdObjectRepository,
sql_client: SqlClient,
) -> None:
"""Tests converting a dataflow plan to a SQL query plan where there is a windowless cumulative metric to compute."""
"""Tests rendering a query for a windowless cumulative metric query with an adjustable time constraint."""
dataflow_plan = dataflow_plan_builder.build_plan(
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name="revenue_all_time"),),
Expand Down Expand Up @@ -195,7 +246,7 @@ def test_cumulative_metric_grain_to_date(
consistent_id_object_repository: ConsistentIdObjectRepository,
sql_client: SqlClient,
) -> None:
"""Tests converting a dataflow plan to a SQL query plan where grain_to_date cumulative metric to compute."""
"""Tests rendering a query against a grain_to_date cumulative metric."""
dataflow_plan = dataflow_plan_builder.build_plan(
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name="revenue_mtd"),),
Expand Down
38 changes: 38 additions & 0 deletions metricflow/test/query_rendering/test_derived_metric_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@

import pytest
from _pytest.fixtures import FixtureRequest
from dbt_semantic_interfaces.implementations.filters.where_filter import PydanticWhereFilter

from metricflow.dataflow.builder.dataflow_plan_builder import DataflowPlanBuilder
from metricflow.plan_conversion.dataflow_to_sql import DataflowToSqlQueryPlanConverter
from metricflow.protocols.sql_client import SqlClient
from metricflow.specs.column_assoc import ColumnAssociationResolver
from metricflow.specs.specs import (
MetricFlowQuerySpec,
MetricSpec,
)
from metricflow.specs.where_filter_transform import WhereSpecFactory
from metricflow.test.fixtures.setup_fixtures import MetricFlowTestSessionState
from metricflow.test.query_rendering.compare_rendered_query import convert_and_check
from metricflow.test.time.metric_time_dimension import MTD_SPEC_DAY, MTD_SPEC_QUARTER, MTD_SPEC_WEEK, MTD_SPEC_YEAR
Expand Down Expand Up @@ -89,6 +92,41 @@ def test_derived_metric_with_offset_window( # noqa: D
)


@pytest.mark.sql_engine_snapshot
def test_derived_metric_with_offset_window_and_time_filter( # noqa: D
request: FixtureRequest,
mf_test_session_state: MetricFlowTestSessionState,
column_association_resolver: ColumnAssociationResolver,
dataflow_plan_builder: DataflowPlanBuilder,
dataflow_to_sql_converter: DataflowToSqlQueryPlanConverter,
sql_client: SqlClient,
) -> None:
dataflow_plan = dataflow_plan_builder.build_plan(
MetricFlowQuerySpec(
metric_specs=(MetricSpec(element_name="bookings_growth_2_weeks"),),
time_dimension_specs=(MTD_SPEC_DAY,),
where_constraint=WhereSpecFactory(
column_association_resolver=column_association_resolver,
).create_from_where_filter(
PydanticWhereFilter(
where_sql_template=(
"{{ TimeDimension('metric_time', 'day') }} = '2020-01-01' "
"or {{ TimeDimension('metric_time', 'day') }} = '2020-01-14'"
)
),
),
)
)

convert_and_check(
request=request,
mf_test_session_state=mf_test_session_state,
dataflow_to_sql_converter=dataflow_to_sql_converter,
sql_client=sql_client,
node=dataflow_plan.sink_output_nodes[0].parent_node,
)


@pytest.mark.sql_engine_snapshot
def test_derived_metric_with_offset_to_grain( # noqa: D
request: FixtureRequest,
Expand Down
Loading

0 comments on commit b812e7a

Please sign in to comment.