refactor: use natural.Compare directly instead of wrapper

This commit is contained in:
Deluan 2026-02-17 09:00:23 -05:00
parent 9bcefea0ca
commit 929e7193b4
3 changed files with 2 additions and 119 deletions

View File

@ -7,13 +7,13 @@ import (
"fmt"
"runtime"
"github.com/maruel/natural"
"github.com/mattn/go-sqlite3"
"github.com/navidrome/navidrome/conf"
_ "github.com/navidrome/navidrome/db/migrations"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/utils/hasher"
"github.com/navidrome/navidrome/utils/singleton"
"github.com/navidrome/navidrome/utils/str"
"github.com/pressly/goose/v3"
)
@ -35,7 +35,7 @@ func Db() *sql.DB {
if err := conn.RegisterFunc("SEEDEDRAND", hasher.HashFunc(), false); err != nil {
return err
}
return conn.RegisterCollation("NATURALSORT", str.NaturalSortCompare)
return conn.RegisterCollation("NATURALSORT", natural.Compare)
},
})
Path = conf.Server.DbPath

View File

@ -1,16 +0,0 @@
package str
import (
"strings"
"github.com/maruel/natural"
)
// NaturalSortCompare compares two strings using natural sort ordering,
// where embedded numeric sequences are compared as numbers rather than
// lexicographically. For example, "track2" < "track10" (unlike lexicographic
// ordering which gives "track10" < "track2"). The comparison is also
// case-insensitive.
func NaturalSortCompare(a, b string) int {
return natural.Compare(strings.ToLower(a), strings.ToLower(b))
}

View File

@ -1,101 +0,0 @@
package str_test
import (
"testing"
"github.com/navidrome/navidrome/utils/str"
)
func TestNaturalSortCompare(t *testing.T) {
tests := []struct {
name string
a, b string
want int // -1, 0, or 1
}{
// Basic string comparison
{"equal strings", "abc", "abc", 0},
{"less than", "abc", "abd", -1},
{"greater than", "abd", "abc", 1},
// Case insensitive
{"case insensitive equal", "ABC", "abc", 0},
{"case insensitive less", "ABC", "abd", -1},
{"case insensitive mixed", "aBc", "AbC", 0},
// Numeric ordering
{"single digit order", "track1", "track2", -1},
{"natural sort 2 vs 10", "track2", "track10", -1},
{"natural sort 9 vs 10", "track9", "track10", -1},
{"natural sort 10 vs 2", "track10", "track2", 1},
{"natural sort 1 vs 100", "track1", "track100", -1},
// Album numbering (main use case from issue)
{"album numbers 1 vs 2", "Bravo Hits 1", "Bravo Hits 2", -1},
{"album numbers 2 vs 10", "Bravo Hits 2", "Bravo Hits 10", -1},
{"album numbers 10 vs 100", "Bravo Hits 10", "Bravo Hits 100", -1},
{"album numbers 9 vs 10", "Bravo Hits 9", "Bravo Hits 10", -1},
{"album numbers equal", "Bravo Hits 10", "Bravo Hits 10", 0},
{"album numbers 99 vs 100", "Bravo Hits 99", "Bravo Hits 100", -1},
{"album numbers 100 vs 101", "Bravo Hits 100", "Bravo Hits 101", -1},
// Pure numeric strings
{"pure number 1 vs 2", "1", "2", -1},
{"pure number 2 vs 10", "2", "10", -1},
{"pure number 10 vs 9", "10", "9", 1},
{"pure number equal", "42", "42", 0},
// Multiple numeric sequences
{"multi number 1.2 vs 1.10", "file1.2", "file1.10", -1},
{"multi number 2.1 vs 10.1", "file2.1", "file10.1", -1},
// Empty strings
{"both empty", "", "", 0},
{"empty vs non-empty", "", "a", -1},
{"non-empty vs empty", "a", "", 1},
// Edge cases
{"same prefix different length", "abc", "abcd", -1},
{"numbers at beginning", "10abc", "9abc", 1},
{"numbers at end", "abc10", "abc9", 1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := str.NaturalSortCompare(tt.a, tt.b)
// Normalize to -1, 0, 1 for comparison
gotNorm := normalize(got)
if gotNorm != tt.want {
t.Errorf("NaturalSortCompare(%q, %q) = %d (normalized: %d), want %d", tt.a, tt.b, got, gotNorm, tt.want)
}
})
}
}
func TestNaturalSortCompare_Symmetry(t *testing.T) {
pairs := [][2]string{
{"track2", "track10"},
{"abc", "abd"},
{"Bravo Hits 9", "Bravo Hits 10"},
{"1", "2"},
{"file1.2", "file1.10"},
}
for _, pair := range pairs {
a, b := pair[0], pair[1]
ab := str.NaturalSortCompare(a, b)
ba := str.NaturalSortCompare(b, a)
if normalize(ab) != -normalize(ba) {
t.Errorf("Symmetry violated: Compare(%q,%q)=%d but Compare(%q,%q)=%d", a, b, ab, b, a, ba)
}
}
}
func normalize(v int) int {
if v < 0 {
return -1
}
if v > 0 {
return 1
}
return 0
}