fix(search): typed text is a literal in every LIKE pattern

The feed's channel-name match and the live search's term fold built their
LIKE pattern from the typed text as it was, so `_` and `%` acted as
wildcards: `build_log` also matched `buildXlog`. The term fold also tested
a substring, so a term already present inside a longer word (`cat` in
`category`) was never folded into search_terms. It now compares whole
terms, escaped.

`like_escape` moves from the Plex query module to `app.utils`, beside
`to_tsquery_str`, and every pattern built from typed text goes through it.
This commit is contained in:
2026-09-19 01:33:32 +02:00
parent 59fd068711
commit 66dbd0af81
6 changed files with 116 additions and 18 deletions
+8
View File
@@ -73,6 +73,14 @@ def to_tsquery_str(q: str) -> str | None:
return " & ".join(tokens)
def like_escape(s: str) -> str:
"""`s` as a LITERAL inside a LIKE/ILIKE pattern: `%` and `_` are wildcards there, and `\\` is
Postgres's default escape character, so all three are escaped. Every pattern built from text a
person typed goes through this — unescaped, a search for `100%` or `my_channel` matches rows it
should not."""
return s.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
def normalize_description(text: str | None) -> str | None:
"""Strip whitespace-only lines from a CHANNEL description before storing. YouTube channel
descriptions pad their contact/link blocks with runs of blank lines; keeping them wastes storage,