From b841c85a02a92be2558cb7f95201d69a0db84b35 Mon Sep 17 00:00:00 2001 From: Max Engine Date: Sun, 26 Jul 2026 03:38:37 -0600 Subject: [PATCH] fix: tag search never matches on slug name The search term was formatted into LOWER(%s) and passed as the *value* of the LIKE, so the function name ended up inside the pattern: slug_name LIKE '%LOWER(coco)%' That can never match. Only the display_name clause did any work, and LIKE is case-sensitive on Postgres, so searching a tag by the name it is written in returns nothing: slug_name=Coco -> matches slug_name=coco -> no match Tags are lower case by convention, so lower case is what users type, and the filter appears to report that no such tag exists. Lower both sides instead. The term normalisation is extracted so it can be covered by a test without a database. --- internal/repo/tag_common/tag_common_repo.go | 22 ++++++++++++++++++--- internal/repo/tag_common/tagsearch_test.go | 17 ++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 internal/repo/tag_common/tagsearch_test.go diff --git a/internal/repo/tag_common/tag_common_repo.go b/internal/repo/tag_common/tag_common_repo.go index c4762b0ca..73c338cc0 100644 --- a/internal/repo/tag_common/tag_common_repo.go +++ b/internal/repo/tag_common/tag_common_repo.go @@ -21,7 +21,6 @@ package tag_common import ( "context" - "fmt" "strconv" "strings" @@ -171,10 +170,20 @@ func (tr *tagCommonRepo) GetTagPage(ctx context.Context, page, pageSize int, tag session := tr.data.DB.Context(ctx) if len(tag.SlugName) > 0 { + // Both sides lowered, so the search is case-insensitive. + // + // This previously read LOWER(%s) formatted against the *search term*, + // which put the function name into the value: the query became + // slug_name LIKE '%LOWER(coco)%' and could never match. Only the + // display_name clause did anything, and that is case-sensitive on + // Postgres, so typing a tag in lower case -- which is how tags are + // written and therefore how anyone types them -- returned nothing at all + // and read as "no such tag". + search := searchTermForTag(tag.SlugName) mainTagCond := builder.And( builder.Or( - builder.Like{"slug_name", fmt.Sprintf("LOWER(%s)", tag.SlugName)}, - builder.Like{"display_name", tag.SlugName}, + builder.Like{"LOWER(slug_name)", search}, + builder.Like{"LOWER(display_name)", search}, ), builder.Eq{"main_tag_id": 0}, ) @@ -293,3 +302,10 @@ func (tr *tagCommonRepo) UpdateTagsAttribute(ctx context.Context, tags []string, } return } + +// searchTermForTag normalises a tag search term. Lowering it here, and lowering +// the columns in the query, is what makes the search case-insensitive: tags are +// written in lower case, so that is how people type them. +func searchTermForTag(term string) string { + return strings.ToLower(strings.TrimSpace(term)) +} diff --git a/internal/repo/tag_common/tagsearch_test.go b/internal/repo/tag_common/tagsearch_test.go new file mode 100644 index 000000000..29cd50cc5 --- /dev/null +++ b/internal/repo/tag_common/tagsearch_test.go @@ -0,0 +1,17 @@ +package tag_common + +import "testing" + +// The bug: the search term was formatted into LOWER(%s), which put the function +// name into the value rather than applying it to the column, so the query became +// slug_name LIKE '%LOWER(coco)%' and matched nothing. Only display_name did any +// work, and that is case-sensitive on Postgres -- so typing a tag the way tags +// are actually written returned "no such tag". +func TestSearchTermIsLoweredNotWrapped(t *testing.T) { + for _, in := range []string{"Coco", "COCO", "coco"} { + got := searchTermForTag(in) + if got != "coco" { + t.Errorf("searchTermForTag(%q) = %q, want %q", in, got, "coco") + } + } +}