fix(server): fix numeric comparisons for float custom tags in smart playlists (#4116)

* Fix numeric comparisons for custom float tags

* feat(criteria): cast numeric tags for sorting and comparisons

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan Quintão
2025-05-25 17:52:27 -04:00
committed by GitHub
parent fac9275c27
commit 5e2db2c673
6 changed files with 69 additions and 6 deletions
+25 -5
View File
@@ -54,11 +54,12 @@ var fieldMap = map[string]*mappedField{
}
type mappedField struct {
field string
order string
isRole bool // true if the field is a role (e.g. "artist", "composer", "conductor", etc.)
isTag bool // true if the field is a tag imported from the file metadata
alias string // name from `mappings.yml` that may differ from the name used in the smart playlist
field string
order string
isRole bool // true if the field is a role (e.g. "artist", "composer", "conductor", etc.)
isTag bool // true if the field is a tag imported from the file metadata
alias string // name from `mappings.yml` that may differ from the name used in the smart playlist
numeric bool // true if the field/tag should be treated as numeric
}
func mapFields(expr map[string]any) map[string]any {
@@ -145,6 +146,12 @@ type tagCond struct {
func (e tagCond) ToSql() (string, []any, error) {
cond, args, err := e.cond.ToSql()
// Check if this tag is marked as numeric in the fieldMap
if fm, ok := fieldMap[e.tag]; ok && fm.numeric {
cond = strings.ReplaceAll(cond, "value", "CAST(value AS REAL)")
}
cond = fmt.Sprintf("exists (select 1 from json_tree(tags, '$.%s') where key='value' and %s)",
e.tag, cond)
if e.not {
@@ -205,3 +212,16 @@ func AddTagNames(tagNames []string) {
}
}
}
// AddNumericTags marks the given tag names as numeric so they can be cast
// when used in comparisons or sorting.
func AddNumericTags(tagNames []string) {
for _, name := range tagNames {
name := strings.ToLower(name)
if fm, ok := fieldMap[name]; ok {
fm.numeric = true
} else {
fieldMap[name] = &mappedField{field: name, isTag: true, numeric: true}
}
}
}