diff --git a/internal/api/resolver_mutation_stash_box.go b/internal/api/resolver_mutation_stash_box.go index d10aa7be6..bbfe8b854 100644 --- a/internal/api/resolver_mutation_stash_box.go +++ b/internal/api/resolver_mutation_stash_box.go @@ -29,7 +29,7 @@ func (r *mutationResolver) SubmitStashBoxFingerprints(ctx context.Context, input var scenes []*models.Scene if err := r.withReadTxn(ctx, func(ctx context.Context) error { - scenes, err = r.sceneService.FindMany(ctx, ids, scene.LoadStashIDs, scene.LoadFiles) + scenes, err = r.sceneService.FindByIDs(ctx, ids, scene.LoadStashIDs, scene.LoadFiles) return err }); err != nil { return false, err diff --git a/internal/manager/repository.go b/internal/manager/repository.go index b8ab564d2..8d4ef1137 100644 --- a/internal/manager/repository.go +++ b/internal/manager/repository.go @@ -15,7 +15,7 @@ type SceneService interface { Merge(ctx context.Context, sourceIDs []int, destinationID int, fileDeleter *scene.FileDeleter, options scene.MergeOptions) error Destroy(ctx context.Context, scene *models.Scene, fileDeleter *scene.FileDeleter, deleteGenerated, deleteFile bool) error - FindMany(ctx context.Context, ids []int, load ...scene.LoadRelationshipOption) ([]*models.Scene, error) + FindByIDs(ctx context.Context, ids []int, load ...scene.LoadRelationshipOption) ([]*models.Scene, error) sceneFingerprintGetter } diff --git a/pkg/models/mocks/SceneReaderWriter.go b/pkg/models/mocks/SceneReaderWriter.go index 954629853..8e4e5ae5a 100644 --- a/pkg/models/mocks/SceneReaderWriter.go +++ b/pkg/models/mocks/SceneReaderWriter.go @@ -549,6 +549,29 @@ func (_m *SceneReaderWriter) FindByGroupID(ctx context.Context, groupID int) ([] return r0, r1 } +// FindByIDs provides a mock function with given fields: ctx, ids +func (_m *SceneReaderWriter) FindByIDs(ctx context.Context, ids []int) ([]*models.Scene, error) { + ret := _m.Called(ctx, ids) + + var r0 []*models.Scene + if rf, ok := ret.Get(0).(func(context.Context, []int) []*models.Scene); ok { + r0 = rf(ctx, ids) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*models.Scene) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, []int) error); ok { + r1 = rf(ctx, ids) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindByOSHash provides a mock function with given fields: ctx, oshash func (_m *SceneReaderWriter) FindByOSHash(ctx context.Context, oshash string) ([]*models.Scene, error) { ret := _m.Called(ctx, oshash) diff --git a/pkg/models/mocks/query.go b/pkg/models/mocks/query.go index dd35d0f86..abde51e65 100644 --- a/pkg/models/mocks/query.go +++ b/pkg/models/mocks/query.go @@ -18,6 +18,10 @@ func (s *sceneResolver) FindMany(ctx context.Context, ids []int) ([]*models.Scen return s.scenes, nil } +func (s *sceneResolver) FindByIDs(ctx context.Context, ids []int) ([]*models.Scene, error) { + return s.scenes, nil +} + func SceneQueryResult(scenes []*models.Scene, count int) *models.SceneQueryResult { ret := models.NewSceneQueryResult(&sceneResolver{ scenes: scenes, diff --git a/pkg/models/repository_scene.go b/pkg/models/repository_scene.go index e28347c5b..f0fff4ac7 100644 --- a/pkg/models/repository_scene.go +++ b/pkg/models/repository_scene.go @@ -10,6 +10,9 @@ type SceneGetter interface { // TODO - rename this to Find and remove existing method FindMany(ctx context.Context, ids []int) ([]*Scene, error) Find(ctx context.Context, id int) (*Scene, error) + // FindByIDs works the same way as FindMany, but it ignores any scenes not found + // Scenes are not guaranteed to be in the same order as the input + FindByIDs(ctx context.Context, ids []int) ([]*Scene, error) } // SceneFinder provides methods to find scenes. diff --git a/pkg/scene/find.go b/pkg/scene/find.go index 3c9d9ac5a..f2f86df59 100644 --- a/pkg/scene/find.go +++ b/pkg/scene/find.go @@ -33,7 +33,31 @@ func LoadFiles(ctx context.Context, scene *models.Scene, r models.SceneReader) e return nil } -// FindMany retrieves multiple scenes by their IDs. +// FindByIDs retrieves multiple scenes by their IDs. +// Missing scenes will be ignored, and the returned scenes are unsorted. +// This method will load the specified relationships for each scene. +func (s *Service) FindByIDs(ctx context.Context, ids []int, load ...LoadRelationshipOption) ([]*models.Scene, error) { + var scenes []*models.Scene + qb := s.Repository + + var err error + scenes, err = qb.FindByIDs(ctx, ids) + if err != nil { + return nil, err + } + + // TODO - we should bulk load these relationships + for _, scene := range scenes { + if err := s.LoadRelationships(ctx, scene, load...); err != nil { + return nil, err + } + } + + return scenes, nil +} + +// FindMany retrieves multiple scenes by their IDs. Return value is guaranteed to be in the same order as the input. +// Missing scenes will return an error. // This method will load the specified relationships for each scene. func (s *Service) FindMany(ctx context.Context, ids []int, load ...LoadRelationshipOption) ([]*models.Scene, error) { var scenes []*models.Scene diff --git a/pkg/sqlite/scene.go b/pkg/sqlite/scene.go index fd1413422..c4a46b23c 100644 --- a/pkg/sqlite/scene.go +++ b/pkg/sqlite/scene.go @@ -493,8 +493,11 @@ func (qb *SceneStore) Find(ctx context.Context, id int) (*models.Scene, error) { return ret, err } -func (qb *SceneStore) FindMany(ctx context.Context, ids []int) ([]*models.Scene, error) { - scenes := make([]*models.Scene, len(ids)) +// FindByIDs finds multiple scenes by their IDs. +// No check is made to see if the scenes exist, and the order of the returned scenes +// is not guaranteed to be the same as the order of the input IDs. +func (qb *SceneStore) FindByIDs(ctx context.Context, ids []int) ([]*models.Scene, error) { + scenes := make([]*models.Scene, 0, len(ids)) table := qb.table() if err := batchExec(ids, defaultBatchSize, func(batch []int) error { @@ -504,16 +507,29 @@ func (qb *SceneStore) FindMany(ctx context.Context, ids []int) ([]*models.Scene, return err } - for _, s := range unsorted { - i := slices.Index(ids, s.ID) - scenes[i] = s - } + scenes = append(scenes, unsorted...) return nil }); err != nil { return nil, err } + return scenes, nil +} + +func (qb *SceneStore) FindMany(ctx context.Context, ids []int) ([]*models.Scene, error) { + scenes := make([]*models.Scene, len(ids)) + + unsorted, err := qb.FindByIDs(ctx, ids) + if err != nil { + return nil, err + } + + for _, s := range unsorted { + i := slices.Index(ids, s.ID) + scenes[i] = s + } + for i := range scenes { if scenes[i] == nil { return nil, fmt.Errorf("scene with id %d not found", ids[i])