Add search toggles, path filters, and find/replace functionality
This commit is contained in:
@@ -759,6 +759,67 @@ def _split_query_tokens(raw: str) -> List[str]:
|
||||
return tokens
|
||||
|
||||
|
||||
def _passes_search_filters(
|
||||
file_info: dict,
|
||||
query_terms: List[str],
|
||||
raw_query: str,
|
||||
case_sensitive: bool,
|
||||
whole_word: bool,
|
||||
regex: bool,
|
||||
include_paths: Optional[str],
|
||||
exclude_paths: Optional[str],
|
||||
) -> bool:
|
||||
"""Post-filter a candidate by case-sensitive, whole-word, regex, and path filters."""
|
||||
title = file_info.get("title", "")
|
||||
content = file_info.get("content", "")
|
||||
path = file_info.get("path", "")
|
||||
search_text = f"{title} {content}"
|
||||
|
||||
# --- Regex mode ---
|
||||
if regex and raw_query:
|
||||
try:
|
||||
flags = 0 if case_sensitive else re.IGNORECASE
|
||||
if whole_word:
|
||||
pattern = re.compile(rf"\b{raw_query}\b", flags)
|
||||
else:
|
||||
pattern = re.compile(raw_query, flags)
|
||||
if not pattern.search(search_text):
|
||||
return False
|
||||
except re.error:
|
||||
return False # invalid regex
|
||||
return _passes_path_filters(path, include_paths, exclude_paths)
|
||||
|
||||
# --- Case-sensitive ---
|
||||
if case_sensitive and query_terms:
|
||||
for term in query_terms:
|
||||
if term not in search_text:
|
||||
return False
|
||||
|
||||
# --- Whole-word ---
|
||||
if whole_word and query_terms:
|
||||
for term in query_terms:
|
||||
pattern = re.compile(rf"\b{re.escape(term)}\b", re.IGNORECASE)
|
||||
if not pattern.search(search_text):
|
||||
return False
|
||||
|
||||
# --- Path filters (glob-like) ---
|
||||
return _passes_path_filters(path, include_paths, exclude_paths)
|
||||
|
||||
|
||||
def _passes_path_filters(path: str, include: Optional[str], exclude: Optional[str]) -> bool:
|
||||
"""Check if a file path passes include/exclude glob patterns."""
|
||||
import fnmatch
|
||||
if include:
|
||||
patterns = [p.strip() for p in include.split(",") if p.strip()]
|
||||
if patterns and not any(fnmatch.fnmatch(path, p) for p in patterns):
|
||||
return False
|
||||
if exclude:
|
||||
patterns = [p.strip() for p in exclude.split(",") if p.strip()]
|
||||
if patterns and any(fnmatch.fnmatch(path, p) for p in patterns):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def advanced_search(
|
||||
query: str,
|
||||
vault_filter: str = "all",
|
||||
@@ -766,6 +827,11 @@ def advanced_search(
|
||||
limit: int = ADVANCED_SEARCH_DEFAULT_LIMIT,
|
||||
offset: int = 0,
|
||||
sort_by: str = "relevance",
|
||||
case_sensitive: bool = False,
|
||||
whole_word: bool = False,
|
||||
regex: bool = False,
|
||||
include_paths: Optional[str] = None,
|
||||
exclude_paths: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Advanced full-text search with TF-IDF scoring, facets, and pagination.
|
||||
|
||||
@@ -923,6 +989,13 @@ def advanced_search(
|
||||
score = 1.0
|
||||
|
||||
if score > 0:
|
||||
# --- Post-filters: case-sensitive, whole-word, regex, path filters ---
|
||||
if not _passes_search_filters(
|
||||
file_info, query_terms, query,
|
||||
case_sensitive, whole_word, regex, include_paths, exclude_paths
|
||||
):
|
||||
continue
|
||||
|
||||
# Build highlighted snippet
|
||||
content = file_info.get("content", "")
|
||||
if has_terms:
|
||||
|
||||
Reference in New Issue
Block a user