Repository: dbt-labs/dbt-core

Issue:
[CT-640] File selectors and support for file selectors as a default method
### Is this your first time opening an issue?

- [X] I have read the [expectations for open source contributors](https://docs.getdbt.com/docs/contributing/oss-expectations)

### Describe the Feature

`dbt should just know that when i say: 
    dbt run -m my_model.sql 
i mean: 
    dbt run -m my_model
`

https://twitter.com/pedram_navid/status/1519160872588898304

### Describe alternatives you've considered

Seemed like a pretty straightforward paper-cut feature request.

### Who will this benefit?

Pedram, apparently

### Are you interested in contributing this feature?

yes!

### Anything else?

_No response_


Discussion:

Resolving patch:
diff --git a/core/dbt/graph/selector_methods.py b/core/dbt/graph/selector_methods.py
--- a/core/dbt/graph/selector_methods.py
+++ b/core/dbt/graph/selector_methods.py
@@ -39,6 +39,7 @@ class MethodName(StrEnum):
     Tag = "tag"
     Source = "source"
     Path = "path"
+    File = "file"
     Package = "package"
     Config = "config"
     TestName = "test_name"
@@ -280,7 +281,7 @@ def search(self, included_nodes: Set[UniqueId], selector: str) -> Iterator[Uniqu
 
 class PathSelectorMethod(SelectorMethod):
     def search(self, included_nodes: Set[UniqueId], selector: str) -> Iterator[UniqueId]:
-        """Yields nodes from inclucded that match the given path."""
+        """Yields nodes from included that match the given path."""
         # use '.' and not 'root' for easy comparison
         root = Path.cwd()
         paths = set(p.relative_to(root) for p in root.glob(selector))
@@ -294,6 +295,14 @@ def search(self, included_nodes: Set[UniqueId], selector: str) -> Iterator[Uniqu
                 yield node
 
 
+class FileSelectorMethod(SelectorMethod):
+    def search(self, included_nodes: Set[UniqueId], selector: str) -> Iterator[UniqueId]:
+        """Yields nodes from included that match the given file name."""
+        for node, real_node in self.all_nodes(included_nodes):
+            if Path(real_node.original_file_path).name == selector:
+                yield node
+
+
 class PackageSelectorMethod(SelectorMethod):
     def search(self, included_nodes: Set[UniqueId], selector: str) -> Iterator[UniqueId]:
         """Yields nodes from included that have the specified package"""
@@ -589,6 +598,7 @@ class MethodManager:
         MethodName.Tag: TagSelectorMethod,
         MethodName.Source: SourceSelectorMethod,
         MethodName.Path: PathSelectorMethod,
+        MethodName.File: FileSelectorMethod,
         MethodName.Package: PackageSelectorMethod,
         MethodName.Config: ConfigSelectorMethod,
         MethodName.TestName: TestNameSelectorMethod,
diff --git a/core/dbt/graph/selector_spec.py b/core/dbt/graph/selector_spec.py
--- a/core/dbt/graph/selector_spec.py
+++ b/core/dbt/graph/selector_spec.py
@@ -80,6 +80,8 @@ def __post_init__(self):
     def default_method(cls, value: str) -> MethodName:
         if _probably_path(value):
             return MethodName.Path
+        elif value.lower().endswith(".sql"):
+            return MethodName.File
         else:
             return MethodName.FQN
 


Test patch:
diff --git a/test/unit/test_graph_selector_methods.py b/test/unit/test_graph_selector_methods.py
--- a/test/unit/test_graph_selector_methods.py
+++ b/test/unit/test_graph_selector_methods.py
@@ -1,4 +1,5 @@
 import copy
+
 import pytest
 from unittest import mock
 
@@ -31,6 +32,7 @@
     TagSelectorMethod,
     SourceSelectorMethod,
     PathSelectorMethod,
+    FileSelectorMethod,
     PackageSelectorMethod,
     ConfigSelectorMethod,
     TestNameSelectorMethod,
@@ -703,6 +705,20 @@ def test_select_path(manifest):
         manifest, method, 'models/missing*')
 
 
+def test_select_file(manifest):
+    methods = MethodManager(manifest, None)
+    method = methods.get_method('file', [])
+    assert isinstance(method, FileSelectorMethod)
+    assert method.arguments == []
+
+    assert search_manifest_using_method(
+        manifest, method, 'table_model.sql') == {'table_model'}
+    assert search_manifest_using_method(
+        manifest, method, 'union_model.sql') == {'union_model', 'mynamespace.union_model'}
+    assert not search_manifest_using_method(
+        manifest, method, 'missing.sql')
+
+
 def test_select_package(manifest):
     methods = MethodManager(manifest, None)
     method = methods.get_method('package', [])