mirror of
https://github.com/navidrome/navidrome.git
synced 2025-12-23 23:18:05 -05:00
fix(scanner): execute GetFolderUpdateInfo in batches to avoid "Expression tree is too large (maximum depth 1000)"
Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
@@ -95,24 +95,57 @@ func (r folderRepository) CountAll(opt ...model.QueryOptions) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r folderRepository) GetFolderUpdateInfo(lib model.Library, targetPaths ...string) (map[string]model.FolderUpdateInfo, error) {
|
func (r folderRepository) GetFolderUpdateInfo(lib model.Library, targetPaths ...string) (map[string]model.FolderUpdateInfo, error) {
|
||||||
|
// If no specific paths, return all folders in the library
|
||||||
|
if len(targetPaths) == 0 {
|
||||||
|
return r.getFolderUpdateInfoAll(lib)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any path is root (return all folders)
|
||||||
|
for _, targetPath := range targetPaths {
|
||||||
|
if targetPath == "" || targetPath == "." {
|
||||||
|
return r.getFolderUpdateInfoAll(lib)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process paths in batches to avoid SQLite's expression tree depth limit (max 1000).
|
||||||
|
// Each path generates ~3 conditions, so batch size of 100 keeps us well under the limit.
|
||||||
|
const batchSize = 100
|
||||||
|
result := make(map[string]model.FolderUpdateInfo)
|
||||||
|
|
||||||
|
for batch := range slices.Chunk(targetPaths, batchSize) {
|
||||||
|
batchResult, err := r.getFolderUpdateInfoBatch(lib, batch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for id, info := range batchResult {
|
||||||
|
result[id] = info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFolderUpdateInfoAll returns update info for all non-missing folders in the library
|
||||||
|
func (r folderRepository) getFolderUpdateInfoAll(lib model.Library) (map[string]model.FolderUpdateInfo, error) {
|
||||||
|
where := And{
|
||||||
|
Eq{"library_id": lib.ID},
|
||||||
|
Eq{"missing": false},
|
||||||
|
}
|
||||||
|
return r.queryFolderUpdateInfo(where)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFolderUpdateInfoBatch returns update info for a batch of target paths and their descendants
|
||||||
|
func (r folderRepository) getFolderUpdateInfoBatch(lib model.Library, targetPaths []string) (map[string]model.FolderUpdateInfo, error) {
|
||||||
where := And{
|
where := And{
|
||||||
Eq{"library_id": lib.ID},
|
Eq{"library_id": lib.ID},
|
||||||
Eq{"missing": false},
|
Eq{"missing": false},
|
||||||
}
|
}
|
||||||
|
|
||||||
// If specific paths are requested, include those folders and all their descendants
|
|
||||||
if len(targetPaths) > 0 {
|
|
||||||
// Collect folder IDs for exact target folders and path conditions for descendants
|
// Collect folder IDs for exact target folders and path conditions for descendants
|
||||||
folderIDs := make([]string, 0, len(targetPaths))
|
folderIDs := make([]string, 0, len(targetPaths))
|
||||||
pathConditions := make(Or, 0, len(targetPaths)*2)
|
pathConditions := make(Or, 0, len(targetPaths)*2)
|
||||||
|
|
||||||
for _, targetPath := range targetPaths {
|
for _, targetPath := range targetPaths {
|
||||||
if targetPath == "" || targetPath == "." {
|
|
||||||
// Root path - include everything in this library
|
|
||||||
pathConditions = Or{}
|
|
||||||
folderIDs = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Clean the path to normalize it. Paths stored in the folder table do not have leading/trailing slashes.
|
// Clean the path to normalize it. Paths stored in the folder table do not have leading/trailing slashes.
|
||||||
cleanPath := strings.TrimPrefix(targetPath, string(os.PathSeparator))
|
cleanPath := strings.TrimPrefix(targetPath, string(os.PathSeparator))
|
||||||
cleanPath = filepath.Clean(cleanPath)
|
cleanPath = filepath.Clean(cleanPath)
|
||||||
@@ -132,8 +165,12 @@ func (r folderRepository) GetFolderUpdateInfo(lib model.Library, targetPaths ...
|
|||||||
} else if len(pathConditions) > 0 {
|
} else if len(pathConditions) > 0 {
|
||||||
where = append(where, pathConditions)
|
where = append(where, pathConditions)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
return r.queryFolderUpdateInfo(where)
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryFolderUpdateInfo executes the query and returns the result map
|
||||||
|
func (r folderRepository) queryFolderUpdateInfo(where And) (map[string]model.FolderUpdateInfo, error) {
|
||||||
sq := r.newSelect().Columns("id", "updated_at", "hash").Where(where)
|
sq := r.newSelect().Columns("id", "updated_at", "hash").Where(where)
|
||||||
var res []struct {
|
var res []struct {
|
||||||
ID string
|
ID string
|
||||||
|
|||||||
Reference in New Issue
Block a user