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") + } + } +}