feat(server): allow multiple sort fields in smart playlists (#4214)
* allow multiple sort fields * Handle invalid sort fields * Update model/criteria/criteria.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
+42
-11
@@ -25,13 +25,39 @@ func (c Criteria) OrderBy() string {
|
|||||||
if c.Sort == "" {
|
if c.Sort == "" {
|
||||||
c.Sort = "title"
|
c.Sort = "title"
|
||||||
}
|
}
|
||||||
sortField := strings.ToLower(c.Sort)
|
|
||||||
|
order := strings.ToLower(strings.TrimSpace(c.Order))
|
||||||
|
if order != "" && order != "asc" && order != "desc" {
|
||||||
|
log.Error("Invalid value in 'order' field. Valid values: 'asc', 'desc'", "order", c.Order)
|
||||||
|
order = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(c.Sort, ",")
|
||||||
|
fields := make([]string, 0, len(parts))
|
||||||
|
|
||||||
|
for _, p := range parts {
|
||||||
|
p = strings.TrimSpace(p)
|
||||||
|
if p == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := "asc"
|
||||||
|
if strings.HasPrefix(p, "+") || strings.HasPrefix(p, "-") {
|
||||||
|
if strings.HasPrefix(p, "-") {
|
||||||
|
dir = "desc"
|
||||||
|
}
|
||||||
|
p = strings.TrimSpace(p[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
sortField := strings.ToLower(p)
|
||||||
f := fieldMap[sortField]
|
f := fieldMap[sortField]
|
||||||
var mapped string
|
|
||||||
if f == nil {
|
if f == nil {
|
||||||
log.Error("Invalid field in 'sort' field. Using 'title'", "sort", c.Sort)
|
log.Error("Invalid field in 'sort' field", "sort", sortField)
|
||||||
mapped = fieldMap["title"].field
|
continue
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
var mapped string
|
||||||
|
|
||||||
if f.order != "" {
|
if f.order != "" {
|
||||||
mapped = f.order
|
mapped = f.order
|
||||||
} else if f.isTag {
|
} else if f.isTag {
|
||||||
@@ -44,15 +70,20 @@ func (c Criteria) OrderBy() string {
|
|||||||
if f.numeric {
|
if f.numeric {
|
||||||
mapped = fmt.Sprintf("CAST(%s AS REAL)", mapped)
|
mapped = fmt.Sprintf("CAST(%s AS REAL)", mapped)
|
||||||
}
|
}
|
||||||
}
|
// If the global 'order' field is set to 'desc', reverse the default or field-specific sort direction.
|
||||||
if c.Order != "" {
|
// This ensures that the global order applies consistently across all fields.
|
||||||
if strings.EqualFold(c.Order, "asc") || strings.EqualFold(c.Order, "desc") {
|
if order == "desc" {
|
||||||
mapped = mapped + " " + c.Order
|
if dir == "asc" {
|
||||||
|
dir = "desc"
|
||||||
} else {
|
} else {
|
||||||
log.Error("Invalid value in 'order' field. Valid values: 'asc', 'desc'", "order", c.Order)
|
dir = "asc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mapped
|
|
||||||
|
fields = append(fields, mapped+" "+dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(fields, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Criteria) ToSql() (sql string, args []any, err error) {
|
func (c Criteria) ToSql() (sql string, args []any, err error) {
|
||||||
|
|||||||
@@ -123,6 +123,28 @@ var _ = Describe("Criteria", func() {
|
|||||||
newObj.Sort = "random"
|
newObj.Sort = "random"
|
||||||
gomega.Expect(newObj.OrderBy()).To(gomega.Equal("random() asc"))
|
gomega.Expect(newObj.OrderBy()).To(gomega.Equal("random() asc"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("sorts by multiple fields", func() {
|
||||||
|
goObj.Sort = "title,-rating"
|
||||||
|
gomega.Expect(goObj.OrderBy()).To(gomega.Equal(
|
||||||
|
"media_file.title asc, COALESCE(annotation.rating, 0) desc",
|
||||||
|
))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("reverts order when order is desc", func() {
|
||||||
|
goObj.Sort = "-date,artist"
|
||||||
|
goObj.Order = "desc"
|
||||||
|
gomega.Expect(goObj.OrderBy()).To(gomega.Equal(
|
||||||
|
"media_file.date asc, COALESCE(json_extract(media_file.participants, '$.artist[0].name'), '') desc",
|
||||||
|
))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("ignores invalid sort fields", func() {
|
||||||
|
goObj.Sort = "bogus,title"
|
||||||
|
gomega.Expect(goObj.OrderBy()).To(gomega.Equal(
|
||||||
|
"media_file.title asc",
|
||||||
|
))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user