- Map number words and ordinals (e.g. "Two"/"2nd") to digit tokens in both SQL normalize_search_text and the Go mirror so "Dune: Part Two" and "Dune Part 2" match - Rebuild title_normalized generated column and title FTS GIN indexes (migration 138)
71 lines
2.5 KiB
Go
71 lines
2.5 KiB
Go
package catalog
|
|
|
|
import "testing"
|
|
|
|
// TestNormalizeTitleForComparison pins the Go mirror of the SQL function
|
|
// public.normalize_search_text(). If Go-side normalization drifts from SQL,
|
|
// ExactTitleHint will fail to match the title_normalized generated column.
|
|
func TestNormalizeTitleForComparison(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
}{
|
|
{"ampersand collapses", "Law & Order", "law order"},
|
|
{"and word strips", "Law and Order", "law order"},
|
|
{"mixed punctuation", "Law and / & Order!", "law order"},
|
|
{"and inside word preserved", "Random Sandwich", "random sandwich"},
|
|
{"leading and stripped", "And Then There Were None", "then there were none"},
|
|
{"trailing and stripped", "Order and", "order"},
|
|
{"only and reduces to empty", "and", ""},
|
|
{"single token kept", "and AND And", ""},
|
|
{"consecutive and tokens", "rock and and roll", "rock roll"},
|
|
{"acronym with ampersand", "S&P 500", "s p 500"},
|
|
{"unicode letters", "Café & Crème", "café crème"},
|
|
{"number word", "Dune: Part Two", "dune part 2"},
|
|
{"number digit", "Dune Part 2", "dune part 2"},
|
|
{"ordinal word", "Second Act", "2 act"},
|
|
{"ordinal digit suffix", "2nd Act", "2 act"},
|
|
{"thirteenth word", "Friday the Thirteenth", "friday the 13"},
|
|
{"thirteenth digit suffix", "Friday the 13th", "friday the 13"},
|
|
{"twenty word", "Twenty", "20"},
|
|
{"composed numbers not folded", "Twenty One", "20 1"},
|
|
{"embedded digit word unchanged", "Se7en", "se7en"},
|
|
{"empty stays empty", "", ""},
|
|
{"whitespace only", " ", ""},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := normalizeTitleForComparison(tc.in)
|
|
if got != tc.want {
|
|
t.Fatalf("normalizeTitleForComparison(%q) = %q, want %q", tc.in, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestParseSearchQuery_ExactTitleHint_NormalizesSearchText ensures that the
|
|
// higher-level parser propagates search normalization into ExactTitleHint, so
|
|
// callers building SQL with the hint get the form that matches title_normalized.
|
|
func TestParseSearchQuery_ExactTitleHint_NormalizesSearchText(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
want string
|
|
}{
|
|
{"Law & Order", "law order"},
|
|
{"Law and Order", "law order"},
|
|
{"\"Law and Order\" 2010", "law order"},
|
|
{"Dune: Part Two", "dune part 2"},
|
|
{"\"Dune: Part Two\" 2024", "dune part 2"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.in, func(t *testing.T) {
|
|
parsed := parseSearchQuery(tc.in)
|
|
if parsed.ExactTitleHint != tc.want {
|
|
t.Fatalf("ExactTitleHint = %q, want %q (parsed = %+v)", parsed.ExactTitleHint, tc.want, parsed)
|
|
}
|
|
})
|
|
}
|
|
}
|