mirror of
https://github.com/immich-app/immich.git
synced 2025-04-21 15:36:26 +02:00
fix(server): skip stacked assets in duplicate detection (#16380)
* skip stacked assets in duplicate detection * update sql * handle stacking after duplicate detection runs
This commit is contained in:
parent
a808b8610e
commit
a708649504
8 changed files with 34 additions and 0 deletions
server
src
migrations
queries
repositories
services
test/fixtures
|
@ -0,0 +1,14 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UnsetStackedAssetsFromDuplicateStatus1740654480319 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
update assets
|
||||
set "duplicateId" = null
|
||||
where "stackId" is not null`);
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
// No need to revert this migration
|
||||
}
|
||||
}
|
|
@ -333,6 +333,7 @@ with
|
|||
and "assets"."duplicateId" is not null
|
||||
and "assets"."deletedAt" is null
|
||||
and "assets"."isVisible" = $2
|
||||
and "assets"."stackId" is null
|
||||
group by
|
||||
"assets"."duplicateId"
|
||||
),
|
||||
|
|
|
@ -112,6 +112,7 @@ with
|
|||
and "assets"."isVisible" = $3
|
||||
and "assets"."type" = $4
|
||||
and "assets"."id" != $5::uuid
|
||||
and "assets"."stackId" is null
|
||||
order by
|
||||
smart_search.embedding <=> $6
|
||||
limit
|
||||
|
|
|
@ -794,6 +794,7 @@ export class AssetRepository {
|
|||
.where('assets.duplicateId', 'is not', null)
|
||||
.where('assets.deletedAt', 'is', null)
|
||||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.stackId', 'is', null)
|
||||
.groupBy('assets.duplicateId'),
|
||||
)
|
||||
.with('unique', (qb) =>
|
||||
|
|
|
@ -318,6 +318,7 @@ export class SearchRepository {
|
|||
.where('assets.isVisible', '=', true)
|
||||
.where('assets.type', '=', type)
|
||||
.where('assets.id', '!=', asUuid(assetId))
|
||||
.where('assets.stackId', 'is', null)
|
||||
.orderBy(sql`smart_search.embedding <=> ${embedding}`)
|
||||
.limit(64),
|
||||
)
|
||||
|
|
|
@ -173,6 +173,16 @@ describe(SearchService.name, () => {
|
|||
expect(mocks.logger.error).toHaveBeenCalledWith(`Asset ${assetStub.image.id} not found`);
|
||||
});
|
||||
|
||||
it('should skip if asset is part of stack', async () => {
|
||||
const id = assetStub.primaryImage.id;
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.primaryImage);
|
||||
|
||||
const result = await sut.handleSearchDuplicates({ id });
|
||||
|
||||
expect(result).toBe(JobStatus.SKIPPED);
|
||||
expect(mocks.logger.debug).toHaveBeenCalledWith(`Asset ${id} is part of a stack, skipping`);
|
||||
});
|
||||
|
||||
it('should skip if asset is not visible', async () => {
|
||||
const id = assetStub.livePhotoMotionAsset.id;
|
||||
mocks.asset.getById.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
||||
|
|
|
@ -59,6 +59,11 @@ export class DuplicateService extends BaseService {
|
|||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
if (asset.stackId) {
|
||||
this.logger.debug(`Asset ${id} is part of a stack, skipping`);
|
||||
return JobStatus.SKIPPED;
|
||||
}
|
||||
|
||||
if (!asset.isVisible) {
|
||||
this.logger.debug(`Asset ${id} is not visible, skipping`);
|
||||
return JobStatus.SKIPPED;
|
||||
|
|
1
server/test/fixtures/asset.stub.ts
vendored
1
server/test/fixtures/asset.stub.ts
vendored
|
@ -184,6 +184,7 @@ export const assetStub = {
|
|||
exifImageHeight: 1000,
|
||||
exifImageWidth: 1000,
|
||||
} as ExifEntity,
|
||||
stackId: 'stack-1',
|
||||
stack: stackStub('stack-1', [
|
||||
{ id: 'primary-asset-id' } as AssetEntity,
|
||||
{ id: 'stack-child-asset-1' } as AssetEntity,
|
||||
|
|
Loading…
Add table
Reference in a new issue