diff --git a/cmdlets/search_file.py b/cmdlets/search_file.py index 54f03a5..bc85d3a 100644 --- a/cmdlets/search_file.py +++ b/cmdlets/search_file.py @@ -216,8 +216,11 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: elif low in {"-type", "--type"} and i + 1 < len(args_list): type_filter = args_list[i + 1].lower() i += 2 - elif not query and not arg.startswith("-"): - query = arg + elif not arg.startswith("-"): + if query: + query += " " + arg + else: + query = arg i += 1 else: i += 1 diff --git a/helper/file_storage.py b/helper/file_storage.py index a1eefdb..0de0315 100644 --- a/helper/file_storage.py +++ b/helper/file_storage.py @@ -24,6 +24,7 @@ from typing import Any, Dict, Optional import sys import shutil import requests +import re from helper.logger import log, debug @@ -286,20 +287,36 @@ class LocalStorageBackend(StorageBackend): query_pattern = f"%{query_lower}%" debug(f"Performing filename/tag search: {query_pattern}") + # Fetch more results than requested to allow for filtering + fetch_limit = (limit or 100) * 50 + cursor.execute(""" SELECT DISTINCT f.id, f.file_path, f.file_size FROM files f WHERE LOWER(f.file_path) LIKE ? ORDER BY f.file_path LIMIT ? - """, (query_pattern, limit or 1000)) + """, (query_pattern, fetch_limit)) rows = cursor.fetchall() - debug(f"Found {len(rows)} filename matches in DB") + debug(f"Found {len(rows)} filename matches in DB (before whole-word filter)") + + # Compile regex for whole word matching + try: + word_regex = re.compile(r'\b' + re.escape(query_lower) + r'\b', re.IGNORECASE) + except Exception: + word_regex = None + seen_files = set() for file_id, file_path_str, size_bytes in rows: if not file_path_str or file_path_str in seen_files: continue + + # Apply whole word filter on filename + if word_regex: + p = Path(file_path_str) + if not word_regex.search(p.name): + continue seen_files.add(file_path_str) file_path = Path(file_path_str) @@ -706,8 +723,23 @@ class HydrusStorageBackend(StorageBackend): }) else: # Free-form search: check if search terms match the title or tags - # Match if ANY search term is found in title or tags (OR logic) - if query_lower == "*" or any(term in all_tags_str or term in title.lower() for term in search_terms): + # Match if ALL search terms are found in title or tags (AND logic) + # AND use whole word matching + + # Combine title and tags for searching + searchable_text = (title + " " + all_tags_str).lower() + + match = True + if query_lower != "*": + for term in search_terms: + # Regex for whole word: \bterm\b + # Escape term to handle special chars + pattern = r'\b' + re.escape(term) + r'\b' + if not re.search(pattern, searchable_text): + match = False + break + + if match: results.append({ "hash": hash_hex, "hash_hex": hash_hex,