Skip to content

Commit

Permalink
Merge branch 'main' into court/dimensions-without-metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
courtneyholcomb committed Sep 27, 2023
2 parents e700c96 + 32f8ae0 commit 8b90082
Show file tree
Hide file tree
Showing 13 changed files with 244 additions and 162 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20230926-140015.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: Removing methods and reordering parameters for Query Interface.
time: 2023-09-26T14:00:15.741015-05:00
custom:
Author: DevonFulcher
Issue: None
36 changes: 20 additions & 16 deletions metricflow/engine/metricflow_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
DataflowToExecutionPlanConverter,
)
from metricflow.plan_conversion.dataflow_to_sql import DataflowToSqlQueryPlanConverter
from metricflow.protocols.query_parameter import QueryParameterDimension, QueryParameterMetric
from metricflow.protocols.query_parameter import GroupByParameter, MetricQueryParameter, OrderByQueryParameter
from metricflow.protocols.sql_client import SqlClient
from metricflow.query.query_exceptions import InvalidQueryException
from metricflow.query.query_parser import MetricFlowQueryParser
Expand Down Expand Up @@ -85,44 +85,47 @@ class MetricFlowQueryRequest:
"""Encapsulates the parameters for a metric query.
metric_names: Names of the metrics to query.
metrics: Metric objects to query.
group_by_names: Names of the dimensions and entities to query.
group_by: Dimension or entity objects to query.
limit: Limit the result to this many rows.
time_constraint_start: Get data for the start of this time range.
time_constraint_end: Get data for the end of this time range.
where_constraint: A SQL string using group by names that can be used like a where clause on the output data.
order_by_names: metric and group by names to order by. A "-" can be used to specify reverse order e.g. "-ds"
order_by_names: metric and group by names to order by. A "-" can be used to specify reverse order e.g. "-ds".
order_by: metric, dimension, or entity objects to order by.
output_table: If specified, output the result data to this table instead of a result dataframe.
sql_optimization_level: The level of optimization for the generated SQL.
query_type: Type of MetricFlow query.
"""

request_id: MetricFlowRequestId
metric_names: Optional[Sequence[str]] = None
metrics: Optional[Sequence[QueryParameterMetric]] = None
metrics: Optional[Sequence[MetricQueryParameter]] = None
group_by_names: Optional[Sequence[str]] = None
group_by: Optional[Sequence[QueryParameterDimension]] = None
group_by: Optional[Tuple[GroupByParameter, ...]] = None
limit: Optional[int] = None
time_constraint_start: Optional[datetime.datetime] = None
time_constraint_end: Optional[datetime.datetime] = None
where_constraint: Optional[str] = None
order_by_names: Optional[Sequence[str]] = None
order_by: Optional[Sequence[QueryParameterDimension]] = None
order_by: Optional[Sequence[OrderByQueryParameter]] = None
output_table: Optional[str] = None
sql_optimization_level: SqlQueryOptimizationLevel = SqlQueryOptimizationLevel.O4
query_type: MetricFlowQueryType = MetricFlowQueryType.METRIC

@staticmethod
def create_with_random_request_id( # noqa: D
metric_names: Optional[Sequence[str]] = None,
metrics: Optional[Sequence[QueryParameterMetric]] = None,
metrics: Optional[Sequence[MetricQueryParameter]] = None,
group_by_names: Optional[Sequence[str]] = None,
group_by: Optional[Sequence[QueryParameterDimension]] = None,
group_by: Optional[Tuple[GroupByParameter, ...]] = None,
limit: Optional[int] = None,
time_constraint_start: Optional[datetime.datetime] = None,
time_constraint_end: Optional[datetime.datetime] = None,
where_constraint: Optional[str] = None,
order_by_names: Optional[Sequence[str]] = None,
order_by: Optional[Sequence[QueryParameterDimension]] = None,
order_by: Optional[Sequence[OrderByQueryParameter]] = None,
output_table: Optional[str] = None,
sql_optimization_level: SqlQueryOptimizationLevel = SqlQueryOptimizationLevel.O4,
query_type: MetricFlowQueryType = MetricFlowQueryType.METRIC,
Expand Down Expand Up @@ -286,9 +289,9 @@ def get_dimension_values(
def explain_get_dimension_values( # noqa: D
self,
metric_names: Optional[List[str]] = None,
metrics: Optional[Sequence[QueryParameterMetric]] = None,
metrics: Optional[Sequence[MetricQueryParameter]] = None,
get_group_by_values: Optional[str] = None,
group_by: Optional[QueryParameterDimension] = None,
group_by: Optional[GroupByParameter] = None,
time_constraint_start: Optional[datetime.datetime] = None,
time_constraint_end: Optional[datetime.datetime] = None,
) -> MetricFlowExplainResult:
Expand Down Expand Up @@ -421,7 +424,7 @@ def _create_execution_plan(self, mf_query_request: MetricFlowQueryRequest) -> Me
time_constraint_start=mf_query_request.time_constraint_start,
time_constraint_end=mf_query_request.time_constraint_end,
where_constraint_str=mf_query_request.where_constraint,
order=mf_query_request.order_by_names,
order_by_names=mf_query_request.order_by_names,
order_by=mf_query_request.order_by,
)
logger.info(f"Query spec is:\n{pformat_big_objects(query_spec)}")
Expand Down Expand Up @@ -461,7 +464,8 @@ def _create_execution_plan(self, mf_query_request: MetricFlowQueryRequest) -> Me
time_constraint_start=mf_query_request.time_constraint_start,
time_constraint_end=mf_query_request.time_constraint_end,
where_constraint_str=mf_query_request.where_constraint,
order=mf_query_request.order_by_names,
order_by_names=mf_query_request.order_by_names,
order_by=mf_query_request.order_by,
)
logger.warning(f"Query spec updated to:\n{pformat_big_objects(query_spec)}")

Expand Down Expand Up @@ -685,9 +689,9 @@ def get_dimension_values( # noqa: D
def explain_get_dimension_values( # noqa: D
self,
metric_names: Optional[List[str]] = None,
metrics: Optional[Sequence[QueryParameterMetric]] = None,
metrics: Optional[Sequence[MetricQueryParameter]] = None,
get_group_by_values: Optional[str] = None,
group_by: Optional[QueryParameterDimension] = None,
group_by: Optional[GroupByParameter] = None,
time_constraint_start: Optional[datetime.datetime] = None,
time_constraint_end: Optional[datetime.datetime] = None,
) -> MetricFlowExplainResult:
Expand All @@ -698,8 +702,8 @@ def explain_get_dimension_values( # noqa: D
MetricFlowQueryRequest.create_with_random_request_id(
metric_names=metric_names,
metrics=metrics,
group_by_names=[get_group_by_values] if get_group_by_values else None,
group_by=[group_by] if group_by else None,
group_by_names=(get_group_by_values,) if get_group_by_values else None,
group_by=(group_by,) if group_by else None,
time_constraint_start=time_constraint_start,
time_constraint_end=time_constraint_end,
query_type=MetricFlowQueryType.DIMENSION_VALUES,
Expand Down
5 changes: 1 addition & 4 deletions metricflow/engine/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,7 @@ def granularity_free_qualified_name(self) -> str:
Dimension set has de-duplicated TimeDimensions such that you never have more than one granularity
in your set for each TimeDimension.
"""
parsed_name = StructuredLinkableSpecName.from_name(qualified_name=self.qualified_name)
return StructuredLinkableSpecName(
entity_link_names=parsed_name.entity_link_names, element_name=self.name
).qualified_name
return StructuredLinkableSpecName.from_name(qualified_name=self.qualified_name).granularity_free_qualified_name


@dataclass(frozen=True)
Expand Down
14 changes: 14 additions & 0 deletions metricflow/naming/linkable_spec_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,17 @@ def entity_prefix(self) -> Optional[str]:
def date_part_suffix(date_part: DatePart) -> str:
"""Suffix used for names with a date_part."""
return f"extract_{date_part.value}"

@property
def granularity_free_qualified_name(self) -> str:
"""Renders the qualified name without the granularity suffix.
In the list metrics and list dimensions outputs we want to render the qualified name of the dimension, but
without including the base granularity for time dimensions. This method is useful in those contexts.
Note: in most cases you should be using the qualified_name - this is only useful in cases where the
Dimension set has de-duplicated TimeDimensions such that you never have more than one granularity
in your set for each TimeDimension.
"""
return StructuredLinkableSpecName(
entity_link_names=self.entity_link_names, element_name=self.element_name
).qualified_name
33 changes: 18 additions & 15 deletions metricflow/protocols/query_interface.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
from __future__ import annotations

from abc import abstractmethod
from typing import Optional, Protocol, Sequence


class QueryInterfaceMetric(Protocol):
"""Represents the interface for Metric in the query interface."""

@abstractmethod
def descending(self, _is_descending: bool) -> QueryInterfaceMetric:
"""Set the sort order for order-by."""
raise NotImplementedError
pass


class QueryInterfaceDimension(Protocol):
"""Represents the interface for Dimension in the query interface."""

@abstractmethod
def grain(self, _grain: str) -> QueryInterfaceDimension:
"""The time granularity."""
raise NotImplementedError

def alias(self, _alias: str) -> QueryInterfaceDimension:
"""Renaming the column."""
raise NotImplementedError
pass

@abstractmethod
def descending(self, _is_descending: bool) -> QueryInterfaceDimension:
"""Set the sort order for order-by."""
pass

@abstractmethod
def date_part(self, _date_part: str) -> QueryInterfaceDimension:
"""Date part to extract from the dimension."""
raise NotImplementedError
pass


class QueryInterfaceDimensionFactory(Protocol):
Expand All @@ -36,9 +38,10 @@ class QueryInterfaceDimensionFactory(Protocol):
Represented as the Dimension constructor in the Jinja sandbox.
"""

@abstractmethod
def create(self, name: str, entity_path: Sequence[str] = ()) -> QueryInterfaceDimension:
"""Create a QueryInterfaceDimension."""
raise NotImplementedError
pass


class QueryInterfaceTimeDimension(Protocol):
Expand All @@ -53,24 +56,23 @@ class QueryInterfaceTimeDimensionFactory(Protocol):
Represented as the TimeDimension constructor in the Jinja sandbox.
"""

@abstractmethod
def create(
self,
time_dimension_name: str,
time_granularity_name: str,
descending: bool = False,
date_part_name: Optional[str] = None,
entity_path: Sequence[str] = (),
descending: Optional[bool] = None,
date_part_name: Optional[str] = None,
) -> QueryInterfaceTimeDimension:
"""Create a TimeDimension."""
raise NotImplementedError
pass


class QueryInterfaceEntity(Protocol):
"""Represents the interface for Entity in the query interface."""

def descending(self, _is_descending: bool) -> QueryInterfaceEntity:
"""Set the sort order for order-by."""
raise NotImplementedError
pass


class QueryInterfaceEntityFactory(Protocol):
Expand All @@ -79,6 +81,7 @@ class QueryInterfaceEntityFactory(Protocol):
Represented as the Entity constructor in the Jinja sandbox.
"""

@abstractmethod
def create(self, entity_name: str, entity_path: Sequence[str] = ()) -> QueryInterfaceEntity:
"""Create an Entity."""
raise NotImplementedError
pass
44 changes: 31 additions & 13 deletions metricflow/protocols/query_parameter.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
from __future__ import annotations

from typing import Optional, Protocol
from typing import Optional, Protocol, Union, runtime_checkable

from dbt_semantic_interfaces.type_enums import TimeGranularity

from metricflow.time.date_part import DatePart


class QueryParameterDimension(Protocol):
"""A query parameter with a grain."""
@runtime_checkable
class MetricQueryParameter(Protocol):
"""Metric requested in a query."""

@property
def name(self) -> str:
"""The name of the item."""
"""The name of the metric."""
raise NotImplementedError


@runtime_checkable
class DimensionOrEntityQueryParameter(Protocol):
"""Generic group by parameter for queries. Might be an entity or a dimension."""

@property
def grain(self) -> Optional[TimeGranularity]:
"""The time granularity."""
def name(self) -> str:
"""The name of the metric."""
raise NotImplementedError


@runtime_checkable
class TimeDimensionQueryParameter(Protocol): # noqa: D
@property
def descending(self) -> bool:
"""Set the sort order for order-by."""
def name(self) -> str:
"""The name of the item."""
raise NotImplementedError

@property
def grain(self) -> Optional[TimeGranularity]:
"""The time granularity."""
raise NotImplementedError

@property
Expand All @@ -31,15 +45,19 @@ def date_part(self) -> Optional[DatePart]:
raise NotImplementedError


class QueryParameterMetric(Protocol):
"""Metric in the query interface."""
GroupByParameter = Union[DimensionOrEntityQueryParameter, TimeDimensionQueryParameter]
InputOrderByParameter = Union[MetricQueryParameter, GroupByParameter]


class OrderByQueryParameter(Protocol):
"""Parameter to order by, specifying ascending or descending."""

@property
def name(self) -> str:
"""The name of the metric."""
def order_by(self) -> InputOrderByParameter:
"""Parameter to order results by."""
raise NotImplementedError

@property
def descending(self) -> bool:
"""Set the sort order for order-by."""
"""Indicates if the order should be ascending or descending."""
raise NotImplementedError
Loading

0 comments on commit 8b90082

Please sign in to comment.