diff --git a/TESTING.md b/TESTING.md index d7616c9..c34daee 100644 --- a/TESTING.md +++ b/TESTING.md @@ -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 diff --git a/database/operations.py b/database/operations.py index 385058a..2c81a71 100644 --- a/database/operations.py +++ b/database/operations.py @@ -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 diff --git a/tests/test_api_workspace_images.py b/tests/test_api_workspace_images.py index 96d770a..131588e 100644 --- a/tests/test_api_workspace_images.py +++ b/tests/test_api_workspace_images.py @@ -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") }, )