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.
This commit is contained in:
Max Engine
2026-07-26 03:38:37 -06:00
committed by LinkinStars
parent 98329f3b05
commit 4488ccc689
2 changed files with 36 additions and 3 deletions
+19 -3
View File
@@ -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))
}
@@ -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")
}
}
}