mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 12:15:47 -04: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
@ -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"."duplicateId" is not null
|
||||||
and "assets"."deletedAt" is null
|
and "assets"."deletedAt" is null
|
||||||
and "assets"."isVisible" = $2
|
and "assets"."isVisible" = $2
|
||||||
|
and "assets"."stackId" is null
|
||||||
group by
|
group by
|
||||||
"assets"."duplicateId"
|
"assets"."duplicateId"
|
||||||
),
|
),
|
||||||
|
@ -112,6 +112,7 @@ with
|
|||||||
and "assets"."isVisible" = $3
|
and "assets"."isVisible" = $3
|
||||||
and "assets"."type" = $4
|
and "assets"."type" = $4
|
||||||
and "assets"."id" != $5::uuid
|
and "assets"."id" != $5::uuid
|
||||||
|
and "assets"."stackId" is null
|
||||||
order by
|
order by
|
||||||
smart_search.embedding <=> $6
|
smart_search.embedding <=> $6
|
||||||
limit
|
limit
|
||||||
|
@ -794,6 +794,7 @@ export class AssetRepository {
|
|||||||
.where('assets.duplicateId', 'is not', null)
|
.where('assets.duplicateId', 'is not', null)
|
||||||
.where('assets.deletedAt', 'is', null)
|
.where('assets.deletedAt', 'is', null)
|
||||||
.where('assets.isVisible', '=', true)
|
.where('assets.isVisible', '=', true)
|
||||||
|
.where('assets.stackId', 'is', null)
|
||||||
.groupBy('assets.duplicateId'),
|
.groupBy('assets.duplicateId'),
|
||||||
)
|
)
|
||||||
.with('unique', (qb) =>
|
.with('unique', (qb) =>
|
||||||
|
@ -318,6 +318,7 @@ export class SearchRepository {
|
|||||||
.where('assets.isVisible', '=', true)
|
.where('assets.isVisible', '=', true)
|
||||||
.where('assets.type', '=', type)
|
.where('assets.type', '=', type)
|
||||||
.where('assets.id', '!=', asUuid(assetId))
|
.where('assets.id', '!=', asUuid(assetId))
|
||||||
|
.where('assets.stackId', 'is', null)
|
||||||
.orderBy(sql`smart_search.embedding <=> ${embedding}`)
|
.orderBy(sql`smart_search.embedding <=> ${embedding}`)
|
||||||
.limit(64),
|
.limit(64),
|
||||||
)
|
)
|
||||||
|
@ -173,6 +173,16 @@ describe(SearchService.name, () => {
|
|||||||
expect(mocks.logger.error).toHaveBeenCalledWith(`Asset ${assetStub.image.id} not found`);
|
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 () => {
|
it('should skip if asset is not visible', async () => {
|
||||||
const id = assetStub.livePhotoMotionAsset.id;
|
const id = assetStub.livePhotoMotionAsset.id;
|
||||||
mocks.asset.getById.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
mocks.asset.getById.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
||||||
|
@ -59,6 +59,11 @@ export class DuplicateService extends BaseService {
|
|||||||
return JobStatus.FAILED;
|
return JobStatus.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (asset.stackId) {
|
||||||
|
this.logger.debug(`Asset ${id} is part of a stack, skipping`);
|
||||||
|
return JobStatus.SKIPPED;
|
||||||
|
}
|
||||||
|
|
||||||
if (!asset.isVisible) {
|
if (!asset.isVisible) {
|
||||||
this.logger.debug(`Asset ${id} is not visible, skipping`);
|
this.logger.debug(`Asset ${id} is not visible, skipping`);
|
||||||
return JobStatus.SKIPPED;
|
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,
|
exifImageHeight: 1000,
|
||||||
exifImageWidth: 1000,
|
exifImageWidth: 1000,
|
||||||
} as ExifEntity,
|
} as ExifEntity,
|
||||||
|
stackId: 'stack-1',
|
||||||
stack: stackStub('stack-1', [
|
stack: stackStub('stack-1', [
|
||||||
{ id: 'primary-asset-id' } as AssetEntity,
|
{ id: 'primary-asset-id' } as AssetEntity,
|
||||||
{ id: 'stack-child-asset-1' } as AssetEntity,
|
{ id: 'stack-child-asset-1' } as AssetEntity,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user