Skip to content
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
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20260917-224607.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: Bare wildcard FQN selectors (e.g. `-s "f_*"`) now match nested models regardless of subfolder depth
time: 2026-09-17T22:46:07.566507+05:30
custom:
Author: itsnamangoyal
Issue: ""
13 changes: 11 additions & 2 deletions core/dbt/graph/selector_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,19 @@ def is_selected_node(fqn: List[str], node_selector: str, is_versioned: bool) ->
# rest of the fqn, this is 100% backwards compatible with the old behavior of
# encountering a wildcard but more expressive in naturally allowing you to
# match the rest of the fqn with more advanced patterns
return fnmatch(
if fnmatch(
".".join(flat_fqn[slurp_from_ix:]),
".".join(node_selector.split(".")[slurp_from_ix:]),
)
):
return True
# A bare wildcard selector (no dot) should also match against just the
# fqn's leaf segment, regardless of how deeply the node is nested. For
# versioned models, the leaf segment is the version tag (e.g. "v4"),
# so match against the model name segment (fqn[-2]) instead.
if "." not in node_selector:
leaf = fqn[-2] if is_versioned else fqn[-1]
return fnmatch(leaf, node_selector)
return False

# if we get all the way down here, then the node is a match
return True
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/graph/test_selector_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,30 @@ def test_select_fqn(manifest):
}


def test_qualified_name_bare_wildcard_matches_root_leaf(manifest):
# Regression guard: bare wildcard selectors (no dot) already work for
# root-level nodes. Documents the asymmetry alongside the nested-leaf case below.
method = QualifiedNameSelectorMethod(manifest, None, [])
assert method.node_is_match("d_*", ["my_pkg", "d_model"], is_versioned=False)


def test_qualified_name_bare_wildcard_matches_nested_leaf(manifest):
# CORE-937: a bare wildcard selector (e.g. "f_*", no dot) should match the
# leaf model name regardless of how deep the model is nested in subfolders.
method = QualifiedNameSelectorMethod(manifest, None, [])
fqn = ["my_pkg", "marts", "subdir", "f_model"]
assert method.node_is_match("f_*", fqn, is_versioned=False)


def test_qualified_name_bare_wildcard_matches_nested_versioned_leaf(manifest):
# CORE-937 follow-up: for a versioned model, the fqn's last segment is the
# version tag (e.g. "v4"), not the model name, so the bare wildcard match
# must fall back to the name segment (fqn[-2]) instead of fqn[-1].
method = QualifiedNameSelectorMethod(manifest, None, [])
fqn = ["my_pkg", "nested_dir", "versioned_model", "v4"]
assert method.node_is_match("versioned_model*", fqn, is_versioned=True)


def test_select_tag(manifest):
methods = MethodManager(manifest, None)
method = methods.get_method("tag", [])
Expand Down
Loading