diff --git a/persistence/album_repository.go b/persistence/album_repository.go index 9c5d5d60..b3165bc1 100644 --- a/persistence/album_repository.go +++ b/persistence/album_repository.go @@ -13,6 +13,7 @@ import ( type albumRepository struct { sqlRepository + sqlRestful } func NewAlbumRepository(ctx context.Context, o orm.Ormer) model.AlbumRepository { diff --git a/persistence/artist_repository.go b/persistence/artist_repository.go index e3411a81..f5ee37d7 100644 --- a/persistence/artist_repository.go +++ b/persistence/artist_repository.go @@ -16,6 +16,7 @@ import ( type artistRepository struct { sqlRepository + sqlRestful indexGroups utils.IndexGroups } diff --git a/persistence/mediafile_repository.go b/persistence/mediafile_repository.go index 38201529..f6c29f4d 100644 --- a/persistence/mediafile_repository.go +++ b/persistence/mediafile_repository.go @@ -14,6 +14,7 @@ import ( type mediaFileRepository struct { sqlRepository + sqlRestful } func NewMediaFileRepository(ctx context.Context, o orm.Ormer) *mediaFileRepository { diff --git a/persistence/player_repository.go b/persistence/player_repository.go index 1edb4f1d..9c33dfb3 100644 --- a/persistence/player_repository.go +++ b/persistence/player_repository.go @@ -11,6 +11,7 @@ import ( type playerRepository struct { sqlRepository + sqlRestful } func NewPlayerRepository(ctx context.Context, o orm.Ormer) model.PlayerRepository { diff --git a/persistence/sql_base_repository.go b/persistence/sql_base_repository.go index caa88646..ba188169 100644 --- a/persistence/sql_base_repository.go +++ b/persistence/sql_base_repository.go @@ -11,19 +11,14 @@ import ( "github.com/astaxie/beego/orm" "github.com/deluan/navidrome/log" "github.com/deluan/navidrome/model" - "github.com/deluan/rest" "github.com/google/uuid" - "github.com/kennygrant/sanitize" ) -type filterFunc = func(field string, value interface{}) Sqlizer - type sqlRepository struct { - ctx context.Context - tableName string - ormer orm.Ormer - sortMappings map[string]string - filterMappings map[string]filterFunc + ctx context.Context + tableName string + ormer orm.Ormer + sortMappings map[string]string } const invalidUserId = "-1" @@ -219,48 +214,3 @@ func (r sqlRepository) logSQL(sql string, args []interface{}, err error, rowsAff log.Trace(r.ctx, "SQL: `"+sql+"`", "args", `[`+strings.Join(fmtArgs, ",")+`]`, "rowsAffected", rowsAffected, "elapsedTime", elapsed) } } - -func (r sqlRepository) parseRestOptions(options ...rest.QueryOptions) model.QueryOptions { - qo := model.QueryOptions{} - if len(options) > 0 { - qo.Sort = options[0].Sort - qo.Order = strings.ToLower(options[0].Order) - qo.Max = options[0].Max - qo.Offset = options[0].Offset - if len(options[0].Filters) > 0 { - filters := And{} - for f, v := range options[0].Filters { - if ff, ok := r.filterMappings[f]; ok { - filters = append(filters, ff(f, v)) - } else { - filters = append(filters, startsWithFilter(f, v)) - } - } - qo.Filters = filters - } - } - return qo -} - -func startsWithFilter(field string, value interface{}) Like { - return Like{field: fmt.Sprintf("%s%%", value)} -} - -func booleanFilter(field string, value interface{}) Sqlizer { - v := strings.ToLower(value.(string)) - return Eq{field: strings.ToLower(v) == "true"} -} - -func fullTextFilter(field string, value interface{}) Sqlizer { - q := value.(string) - q = strings.TrimSpace(sanitize.Accents(strings.ToLower(strings.TrimSuffix(q, "*")))) - parts := strings.Split(q, " ") - filters := And{} - for _, part := range parts { - filters = append(filters, Or{ - Like{"full_text": part + "%"}, - Like{"full_text": "%" + part + "%"}, - }) - } - return filters -} diff --git a/persistence/sql_restful.go b/persistence/sql_restful.go new file mode 100644 index 00000000..3b2335ff --- /dev/null +++ b/persistence/sql_restful.go @@ -0,0 +1,62 @@ +package persistence + +import ( + "fmt" + "strings" + + . "github.com/Masterminds/squirrel" + "github.com/deluan/navidrome/model" + "github.com/deluan/rest" + "github.com/kennygrant/sanitize" +) + +type filterFunc = func(field string, value interface{}) Sqlizer + +type sqlRestful struct { + filterMappings map[string]filterFunc +} + +func (r sqlRestful) parseRestOptions(options ...rest.QueryOptions) model.QueryOptions { + qo := model.QueryOptions{} + if len(options) > 0 { + qo.Sort = options[0].Sort + qo.Order = strings.ToLower(options[0].Order) + qo.Max = options[0].Max + qo.Offset = options[0].Offset + if len(options[0].Filters) > 0 { + filters := And{} + for f, v := range options[0].Filters { + if ff, ok := r.filterMappings[f]; ok { + filters = append(filters, ff(f, v)) + } else { + filters = append(filters, startsWithFilter(f, v)) + } + } + qo.Filters = filters + } + } + return qo +} + +func startsWithFilter(field string, value interface{}) Like { + return Like{field: fmt.Sprintf("%s%%", value)} +} + +func booleanFilter(field string, value interface{}) Sqlizer { + v := strings.ToLower(value.(string)) + return Eq{field: strings.ToLower(v) == "true"} +} + +func fullTextFilter(field string, value interface{}) Sqlizer { + q := value.(string) + q = strings.TrimSpace(sanitize.Accents(strings.ToLower(strings.TrimSuffix(q, "*")))) + parts := strings.Split(q, " ") + filters := And{} + for _, part := range parts { + filters = append(filters, Or{ + Like{"full_text": part + "%"}, + Like{"full_text": "%" + part + "%"}, + }) + } + return filters +} diff --git a/persistence/transcoding_repository.go b/persistence/transcoding_repository.go index c51c5b99..b4f19ae8 100644 --- a/persistence/transcoding_repository.go +++ b/persistence/transcoding_repository.go @@ -11,6 +11,7 @@ import ( type transcodingRepository struct { sqlRepository + sqlRestful } func NewTranscodingRepository(ctx context.Context, o orm.Ormer) model.TranscodingRepository { diff --git a/persistence/user_repository.go b/persistence/user_repository.go index 969720aa..f183e5f8 100644 --- a/persistence/user_repository.go +++ b/persistence/user_repository.go @@ -14,6 +14,7 @@ import ( type userRepository struct { sqlRepository + sqlRestful } func NewUserRepository(ctx context.Context, o orm.Ormer) model.UserRepository {