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

Expand test coverage for cumulative and derived offset metrics #856

Merged
merged 3 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -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
Loading