fix(images): search never matches a pair's own variant title

Search covers the filename, the linked positive's text and Library title,
and its tags (#249 D12). The pair title names a negative variant, so a
variant name alone no longer admits the positive's images.
This commit is contained in:
Vito Sansevero
2026-10-20 07:55:09 -07:00
parent e84a002820
commit 92159ef78f
3 changed files with 25 additions and 15 deletions
+5 -3
View File
@@ -509,9 +509,11 @@ and skip the parse.
Filters (`mode=workspace`; anything invalid is 400 "Invalid gallery filters"):
- `q` (at most 500 characters). An entry passes when the case-folded relative path
contains it, or when its linked positive's text, Library title, a tag name, or
its pair title contains it (SQLite `LIKE` with escaping, ASCII case folding
only). Search does not apply the card title gate, and does not search pair tags.
contains it, or when its linked positive's text, Library title or a tag name
contains it (SQLite `LIKE` with escaping, ASCII case folding only). A pair's own
title names its negative variant, so it is not searched (#249 D12): a variant
name alone matches nothing. Search does not apply the card title gate, and does
not search pair tags.
- `category` (exact and case-sensitive, at most 200 characters, stripped; blank
means none) and `tags` (a comma list, blanks dropped, order-free, AND, at most 30
tags of at most 200 characters) admit linked images only, through the linked
+7 -6
View File
@@ -213,15 +213,15 @@ JOIN prompts p ON p.id = COALESCE(pp.positive_prompt_id, gi.prompt_id)
WHERE gi.file_path IS NOT NULL AND CASE WHEN {admit} THEN NOT EXISTS (
SELECT 1 FROM generated_images other
WHERE other.file_path = gi.file_path AND other.id <> gi.id) ELSE 0 END"""
# The positive's text, its Library title or one of its tag names, or the pair
# title, contains :pattern (LIKE, so ASCII case is ignored as in Library search).
_LINK_FILTER_HIT = """(p.id IN (
# The positive's text, its Library title or one of its tag names contains
# :pattern (LIKE, so ASCII case is ignored as in Library search). A pair's own
# title names its negative variant and is never searched (#249 D12).
_LINK_FILTER_HIT = """p.id IN (
SELECT hp.id FROM prompts hp
LEFT JOIN prompt_library_state hls ON hls.prompt_id = hp.id
WHERE hp.text LIKE :pattern ESCAPE '\\' OR hls.title LIKE :pattern ESCAPE '\\'
OR hp.id IN (SELECT ht.prompt_id FROM prompt_tags ht JOIN tags hn
ON hn.id = ht.tag_id WHERE hn.name LIKE :pattern ESCAPE '\\'))
OR pp.title LIKE :pattern ESCAPE '\\')"""
ON hn.id = ht.tag_id WHERE hn.name LIKE :pattern ESCAPE '\\'))"""
_LINK_FILTER_TAG = (
"p.id IN (SELECT ft.prompt_id FROM prompt_tags ft JOIN tags fn "
"ON fn.id = ft.tag_id WHERE fn.name = :tag{index})"
@@ -2340,7 +2340,8 @@ class PromptDatabase(
Rows are limited to images whose linked positive has *category* and
every tag in *tags* (exact names). ``hit`` says whether *q* occurs in
the positive's text, Library title or a tag name, or in the pair title.
the positive's text, Library title or a tag name; a pair's own title
names its negative variant and is not searched.
Without category or tags only hits are returned, unless *every_link*
asks for every linked image (the rating sort and the positive count).
The positive id is the card's linked positive. One SELECT whose rows
+13 -6
View File
@@ -1068,8 +1068,9 @@ class TestWorkspaceImages(ImageAPITestCase):
cases = (
# Six unambiguous positives; the ambiguous file and unlinked count none.
({}, 10, 6),
# Text, title, tag, pair title (its positive) and the filename match.
({"q": "ZEBRA"}, 6, 4),
# Text, stored title, tag and the filename match (never a pair's
# own variant title, D12).
({"q": "ZEBRA"}, 5, 3),
({"category": "Night"}, 4, 3),
({"tags": "a,b"}, 3, 2),
({"q": "plain_unlinked"}, 1, 0),
@@ -1200,18 +1201,24 @@ class TestWorkspaceImages(ImageAPITestCase):
files = self._filter_fixtures()
_, data = await self.get(q="ZEBRA", sort="name")
# Text, stored title, tag, pair title and filename; never an ambiguous link.
# Text, stored title, tag and filename; never an ambiguous link.
self.assertEqual(
self._names(data),
[
"day_ab.png",
"night_ab.png",
"paired.png",
"titled.png",
"zebra_unlinked.png",
],
)
self.assertEqual(data["total"], 5)
self.assertEqual(data["total"], 4)
# D12: a pair's own title names its negative variant, so the variant
# name alone matches nothing; the linked positive's text still does.
_, data = await self.get(q="Zebra pair")
self.assertEqual(self._names(data), [])
self.assertEqual(data["total"], 0)
_, data = await self.get(q="pair positive")
self.assertEqual(self._names(data), ["paired.png"])
_, data = await self.get(q="0% w")
self.assertEqual(self._names(data), ["percent.png"])
_, data = await self.get(q="%")
@@ -1222,7 +1229,7 @@ class TestWorkspaceImages(ImageAPITestCase):
self.api.db.get_image_paths_matching("zebra", None, []),
{
normalize_image_path(str(files[name]))
for name in ("night_ab", "day_ab", "titled", "paired")
for name in ("night_ab", "day_ab", "titled")
},
)