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

Dc/8546 semantic models in graph selection #8589

Merged
merged 10 commits into from
Sep 26, 2023
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20230925-233306.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: semantic models in graph selection
time: 2023-09-25T23:33:06.754344+01:00
custom:
Author: dave-connors-3 michelleark
Issue: "8589"
1 change: 1 addition & 0 deletions core/dbt/cli/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@
type=ChoiceTuple(
[
"metric",
"semantic_model",
"source",
"analysis",
"model",
Expand Down
4 changes: 2 additions & 2 deletions core/dbt/compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,10 @@ def link_node(self, node: GraphMemberNode, manifest: Manifest):
def link_graph(self, manifest: Manifest):
for source in manifest.sources.values():
self.add_node(source.unique_id)
for semantic_model in manifest.semantic_models.values():
self.add_node(semantic_model.unique_id)
for node in manifest.nodes.values():
self.link_node(node, manifest)
for semantic_model in manifest.semantic_models.values():
self.link_node(semantic_model, manifest)
for exposure in manifest.exposures.values():
self.link_node(exposure, manifest)
for metric in manifest.metrics.values():
Expand Down
50 changes: 50 additions & 0 deletions core/dbt/contracts/graph/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,56 @@
else None
)

def same_model(self, old: "SemanticModel") -> bool:
return self.model == old.same_model

Check warning on line 1680 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1680

Added line #L1680 was not covered by tests

def same_node_relation(self, old: "SemanticModel") -> bool:
return self.node_relation == old.node_relation

Check warning on line 1683 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1683

Added line #L1683 was not covered by tests

def same_description(self, old: "SemanticModel") -> bool:
return self.description == old.description

Check warning on line 1686 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1686

Added line #L1686 was not covered by tests

def same_defaults(self, old: "SemanticModel") -> bool:
return self.defaults == old.defaults

Check warning on line 1689 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1689

Added line #L1689 was not covered by tests

def same_entities(self, old: "SemanticModel") -> bool:
return self.entities == old.entities

Check warning on line 1692 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1692

Added line #L1692 was not covered by tests

def same_dimensions(self, old: "SemanticModel") -> bool:
return self.dimensions == old.dimensions

Check warning on line 1695 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1695

Added line #L1695 was not covered by tests

def same_measures(self, old: "SemanticModel") -> bool:
return self.measures == old.measures

Check warning on line 1698 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1698

Added line #L1698 was not covered by tests

def same_config(self, old: "SemanticModel") -> bool:
return self.config == old.config

Check warning on line 1701 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1701

Added line #L1701 was not covered by tests

def same_primary_entity(self, old: "SemanticModel") -> bool:
return self.primary_entity == old.primary_entity

Check warning on line 1704 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1704

Added line #L1704 was not covered by tests

def same_group(self, old: "SemanticModel") -> bool:
return self.group == old.group

Check warning on line 1707 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1707

Added line #L1707 was not covered by tests

def same_contents(self, old: Optional["SemanticModel"]) -> bool:
# existing when it didn't before is a change!
# metadata/tags changes are not "changes"
if old is None:
return True

return (

Check warning on line 1715 in core/dbt/contracts/graph/nodes.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/nodes.py#L1715

Added line #L1715 was not covered by tests
self.same_model(old)
and self.same_node_relation(old)
and self.same_description(old)
and self.same_defaults(old)
and self.same_entities(old)
and self.same_dimensions(old)
and self.same_measures(old)
and self.same_config(old)
and self.same_primary_entity(old)
and self.same_group(old)
and True
)


# ====================================
# Patches
Expand Down
2 changes: 1 addition & 1 deletion core/dbt/graph/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

INTERSECTION_DELIMITER = ","

DEFAULT_INCLUDES: List[str] = ["fqn:*", "source:*", "exposure:*", "metric:*"]
DEFAULT_INCLUDES: List[str] = ["fqn:*", "source:*", "exposure:*", "metric:*", "semantic_model:*"]
DEFAULT_EXCLUDES: List[str] = []


Expand Down
48 changes: 44 additions & 4 deletions core/dbt/graph/selector_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ResultNode,
ManifestNode,
ModelNode,
SemanticModel,
)
from dbt.contracts.graph.unparsed import UnparsedVersion
from dbt.contracts.state import PreviousState
Expand Down Expand Up @@ -53,6 +54,7 @@
SourceStatus = "source_status"
Wildcard = "wildcard"
Version = "version"
SemanticModel = "semantic_model"


def is_selected_node(fqn: List[str], node_selector: str, is_versioned: bool) -> bool:
Expand Down Expand Up @@ -144,6 +146,16 @@
continue
yield unique_id, metric

def semantic_model_nodes(
self, included_nodes: Set[UniqueId]
) -> Iterator[Tuple[UniqueId, SemanticModel]]:

for key, semantic_model in self.manifest.semantic_models.items():
unique_id = UniqueId(key)
if unique_id not in included_nodes:
continue

Check warning on line 156 in core/dbt/graph/selector_methods.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/graph/selector_methods.py#L156

Added line #L156 was not covered by tests
yield unique_id, semantic_model

def all_nodes(
self, included_nodes: Set[UniqueId]
) -> Iterator[Tuple[UniqueId, SelectorTarget]]:
Expand All @@ -152,6 +164,7 @@
self.source_nodes(included_nodes),
self.exposure_nodes(included_nodes),
self.metric_nodes(included_nodes),
self.semantic_model_nodes(included_nodes),
)

def configurable_nodes(
Expand All @@ -167,6 +180,7 @@
self.parsed_nodes(included_nodes),
self.exposure_nodes(included_nodes),
self.metric_nodes(included_nodes),
self.semantic_model_nodes(included_nodes),
)

def groupable_nodes(
Expand Down Expand Up @@ -210,8 +224,8 @@

:param str selector: The selector or node name
"""
parsed_nodes = list(self.parsed_nodes(included_nodes))
for node, real_node in parsed_nodes:
non_source_nodes = list(self.non_source_nodes(included_nodes))
for node, real_node in non_source_nodes:
if self.node_is_match(selector, real_node.fqn, real_node.is_versioned):
yield node

Expand Down Expand Up @@ -322,6 +336,31 @@
yield node


class SemanticModelSelectorMethod(SelectorMethod):
def search(self, included_nodes: Set[UniqueId], selector: str) -> Iterator[UniqueId]:
parts = selector.split(".")
target_package = SELECTOR_GLOB
if len(parts) == 1:
target_name = parts[0]
elif len(parts) == 2:
target_package, target_name = parts

Check warning on line 346 in core/dbt/graph/selector_methods.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/graph/selector_methods.py#L345-L346

Added lines #L345 - L346 were not covered by tests
else:
msg = (

Check warning on line 348 in core/dbt/graph/selector_methods.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/graph/selector_methods.py#L348

Added line #L348 was not covered by tests
'Invalid semantic model selector value "{}". Semantic models must be of '
"the form ${{semantic_model_name}} or "
"${{semantic_model_package.semantic_model_name}}"
).format(selector)
raise DbtRuntimeError(msg)

Check warning on line 353 in core/dbt/graph/selector_methods.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/graph/selector_methods.py#L353

Added line #L353 was not covered by tests

for node, real_node in self.semantic_model_nodes(included_nodes):
if not fnmatch(real_node.package_name, target_package):
continue

Check warning on line 357 in core/dbt/graph/selector_methods.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/graph/selector_methods.py#L357

Added line #L357 was not covered by tests
if not fnmatch(real_node.name, target_name):
continue

yield node


class PathSelectorMethod(SelectorMethod):
def search(self, included_nodes: Set[UniqueId], selector: str) -> Iterator[UniqueId]:
"""Yields nodes from included that match the given path."""
Expand Down Expand Up @@ -431,7 +470,7 @@
resource_type = NodeType(selector)
except ValueError as exc:
raise DbtRuntimeError(f'Invalid resource_type selector "{selector}"') from exc
for node, real_node in self.parsed_nodes(included_nodes):
for node, real_node in self.all_nodes(included_nodes):

Check warning on line 473 in core/dbt/graph/selector_methods.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/graph/selector_methods.py#L473

Added line #L473 was not covered by tests
if real_node.resource_type == resource_type:
yield node

Expand Down Expand Up @@ -539,7 +578,7 @@
def check_modified_content(
self, old: Optional[SelectorTarget], new: SelectorTarget, adapter_type: str
) -> bool:
if isinstance(new, (SourceDefinition, Exposure, Metric)):
if isinstance(new, (SourceDefinition, Exposure, Metric, SemanticModel)):
# these all overwrite `same_contents`
different_contents = not new.same_contents(old) # type: ignore
else:
Expand Down Expand Up @@ -761,6 +800,7 @@
MethodName.Result: ResultSelectorMethod,
MethodName.SourceStatus: SourceStatusSelectorMethod,
MethodName.Version: VersionSelectorMethod,
MethodName.SemanticModel: SemanticModelSelectorMethod,
}

def __init__(
Expand Down
9 changes: 8 additions & 1 deletion core/dbt/task/list.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json

from dbt.contracts.graph.nodes import Exposure, SourceDefinition, Metric
from dbt.contracts.graph.nodes import Exposure, SourceDefinition, Metric, SemanticModel
from dbt.flags import get_flags
from dbt.graph import ResourceTypeSelector
from dbt.task.runnable import GraphRunnableTask
Expand Down Expand Up @@ -28,6 +28,7 @@ class ListTask(GraphRunnableTask):
NodeType.Source,
NodeType.Exposure,
NodeType.Metric,
NodeType.SemanticModel,
)
)
ALL_RESOURCE_VALUES = DEFAULT_RESOURCE_VALUES | frozenset((NodeType.Analysis,))
Expand Down Expand Up @@ -74,6 +75,8 @@ def _iterate_selected_nodes(self):
yield self.manifest.exposures[node]
elif node in self.manifest.metrics:
yield self.manifest.metrics[node]
elif node in self.manifest.semantic_models:
yield self.manifest.semantic_models[node]
else:
raise DbtRuntimeError(
f'Got an unexpected result from node selection: "{node}"'
Expand All @@ -97,6 +100,10 @@ def generate_selectors(self):
# metrics are searched for by pkg.metric_name
metric_selector = ".".join([node.package_name, node.name])
yield f"metric:{metric_selector}"
elif node.resource_type == NodeType.SemanticModel:
assert isinstance(node, SemanticModel)
semantic_model_selector = ".".join([node.package_name, node.name])
yield f"semantic_model:{semantic_model_selector}"
else:
# everything else is from `fqn`
yield ".".join(node.fqn)
Expand Down
56 changes: 55 additions & 1 deletion tests/functional/list/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,16 @@

{{ config(materialized='ephemeral') }}

select 1 as id
select
1 as id,
{{ dbt.date_trunc('day', dbt.current_timestamp()) }} as created_at

"""

models__metric_flow = """

select
{{ dbt.date_trunc('day', dbt.current_timestamp()) }} as date_day

"""

Expand Down Expand Up @@ -103,6 +112,38 @@

"""

semantic_models__sm_yml = """
semantic_models:
- name: my_sm
model: ref('outer')
defaults:
agg_time_dimension: created_at
entities:
- name: my_entity
type: primary
expr: id
dimensions:
- name: created_at
type: time
type_params:
time_granularity: day
measures:
- name: total_outer_count
agg: count
expr: 1

"""

metrics__m_yml = """
metrics:
- name: total_outer
type: simple
description: The total count of outer
label: Total Outer
type_params:
measure: total_outer_count
"""


@pytest.fixture(scope="class")
def snapshots():
Expand All @@ -122,6 +163,9 @@ def models():
"incremental.sql": models__incremental_sql,
"docs.md": models__docs_md,
"outer.sql": models__outer_sql,
"metricflow_time_spine.sql": models__metric_flow,
"sm.yml": semantic_models__sm_yml,
"m.yml": metrics__m_yml,
"sub": {"inner.sql": models__sub__inner_sql},
}

Expand All @@ -141,6 +185,16 @@ def analyses():
return {"a.sql": analyses__a_sql}


@pytest.fixture(scope="class")
def semantic_models():
return {"sm.yml": semantic_models__sm_yml}


@pytest.fixture(scope="class")
def metrics():
return {"m.yml": metrics__m_yml}


@pytest.fixture(scope="class")
def project_files(
project_root,
Expand Down
Loading
Loading