mirror of
https://github.com/stashapp/stash.git
synced 2026-03-29 09:32:17 +02:00
fix: resolve image duplicate finder issues
- Wrap FindDuplicateImages query in r.withReadTxn() to ensure a database transaction in context. - Use queryFunc instead of queryStruct for fetching multiple hashes, preventing runtime errors. - Fix N+1 query issue in duplicate grouping by using qb.FindMany() instead of qb.Find() for each duplicate image. - Revert searchColumns array to exclude "images.details" which was from another PR and remove related failing test.
This commit is contained in:
parent
8358c794ba
commit
9295655d19
3 changed files with 25 additions and 31 deletions
|
|
@ -135,6 +135,13 @@ func (r *queryResolver) AllImages(ctx context.Context) (ret []*models.Image, err
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) FindDuplicateImages(ctx context.Context, distance int) ([][]*models.Image, error) {
|
||||
return r.repository.Image.FindDuplicates(ctx, distance)
|
||||
func (r *queryResolver) FindDuplicateImages(ctx context.Context, distance int) (ret [][]*models.Image, err error) {
|
||||
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
|
||||
ret, err = r.repository.Image.FindDuplicates(ctx, distance)
|
||||
return err
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -838,7 +838,7 @@ func (qb *ImageStore) makeQuery(ctx context.Context, imageFilter *models.ImageFi
|
|||
)
|
||||
|
||||
filepathColumn := "folders.path || '" + string(filepath.Separator) + "' || files.basename"
|
||||
searchColumns := []string{"images.title", "images.details", filepathColumn, "files_fingerprints.fingerprint"}
|
||||
searchColumns := []string{"images.title", filepathColumn, "files_fingerprints.fingerprint"}
|
||||
query.parseQueryString(searchColumns, *q)
|
||||
}
|
||||
|
||||
|
|
@ -1104,29 +1104,30 @@ func (qb *ImageStore) FindDuplicates(ctx context.Context, distance int) ([][]*mo
|
|||
WHERE files_fingerprints.type = 'phash'`
|
||||
|
||||
var hashes []*utils.Phash
|
||||
err := imageRepository.queryStruct(ctx, query, nil, &hashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := imageRepository.queryFunc(ctx, query, nil, false, func(rows *sqlx.Rows) error {
|
||||
phash := utils.Phash{
|
||||
Bucket: -1,
|
||||
Duration: -1,
|
||||
}
|
||||
if err := rows.StructScan(&phash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, h := range hashes {
|
||||
h.Bucket = -1
|
||||
hashes = append(hashes, &phash)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dupeIds := utils.FindDuplicates(hashes, distance, -1)
|
||||
|
||||
var result [][]*models.Image
|
||||
for _, comp := range dupeIds {
|
||||
var group []*models.Image
|
||||
for _, id := range comp {
|
||||
img, err := qb.Find(ctx, id)
|
||||
if err == nil && img != nil {
|
||||
group = append(group, img)
|
||||
if images, err := qb.FindMany(ctx, comp); err == nil {
|
||||
if len(images) > 1 {
|
||||
result = append(result, images)
|
||||
}
|
||||
}
|
||||
if len(group) > 1 {
|
||||
result = append(result, group)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
|
|||
|
|
@ -1596,20 +1596,6 @@ func TestImageQueryQ(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestImageQueryQ_Details(t *testing.T) {
|
||||
withTxn(func(ctx context.Context) error {
|
||||
const imageIdx = 3
|
||||
|
||||
q := getImageStringValue(imageIdx, detailsField)
|
||||
|
||||
sqb := db.Image
|
||||
|
||||
imageQueryQ(ctx, t, sqb, q, imageIdx)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func queryImagesWithCount(ctx context.Context, sqb models.ImageReader, imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, int, error) {
|
||||
result, err := sqb.Query(ctx, models.ImageQueryOptions{
|
||||
QueryOptions: models.QueryOptions{
|
||||
|
|
|
|||
Loading…
Reference in a new issue