mirror of
https://github.com/Art051/immich.git
synced 2025-08-11 19:29:00 +00:00
perf(server): optimize getByIds query (#7918)
* clean up usage * i'm not updating all these tests * update tests * add indices * add indices to entities remove index from person entity add to face entity fix * simplify query * update sql * missing await * remove synchronize false
This commit is contained in:
@@ -3,6 +3,7 @@ import { AssetEntity } from './asset.entity';
|
||||
import { PersonEntity } from './person.entity';
|
||||
|
||||
@Entity('asset_faces', { synchronize: false })
|
||||
@Index('IDX_asset_faces_assetId_personId', ['assetId', 'personId'])
|
||||
@Index(['personId', 'assetId'])
|
||||
export class AssetFaceEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
|
||||
@@ -35,6 +35,7 @@ export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_library_checksum';
|
||||
@Index('IDX_day_of_month', { synchronize: false })
|
||||
@Index('IDX_month', { synchronize: false })
|
||||
@Index('IDX_originalPath_libraryId', ['originalPath', 'libraryId'])
|
||||
@Index('IDX_asset_id_stackId', ['id', 'stackId'])
|
||||
@Index('idx_originalFileName_trigram', { synchronize: false })
|
||||
// For all assets, each originalpath must be unique per user and library
|
||||
export class AssetEntity {
|
||||
@@ -145,7 +146,7 @@ export class AssetEntity {
|
||||
smartSearch?: SmartSearchEntity;
|
||||
|
||||
@ManyToMany(() => TagEntity, (tag) => tag.assets, { cascade: true })
|
||||
@JoinTable({ name: 'tag_asset' })
|
||||
@JoinTable({ name: 'tag_asset', synchronize: false })
|
||||
tags!: TagEntity[];
|
||||
|
||||
@ManyToMany(() => SharedLinkEntity, (link) => link.assets, { cascade: true })
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class AddAssetRelationIndices1710293990203 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`);
|
||||
}
|
||||
}
|
||||
@@ -137,8 +137,20 @@ export class AssetRepository implements IAssetRepository {
|
||||
relations?: FindOptionsRelations<AssetEntity>,
|
||||
select?: FindOptionsSelect<AssetEntity>,
|
||||
): Promise<AssetEntity[]> {
|
||||
if (!relations) {
|
||||
relations = {
|
||||
return this.repository.find({
|
||||
where: { id: In(ids) },
|
||||
relations,
|
||||
select,
|
||||
withDeleted: true,
|
||||
});
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [[DummyValue.UUID]] })
|
||||
@ChunkedArray()
|
||||
getByIdsWithAllRelations(ids: string[]): Promise<AssetEntity[]> {
|
||||
return this.repository.find({
|
||||
where: { id: In(ids) },
|
||||
relations: {
|
||||
exifInfo: true,
|
||||
smartInfo: true,
|
||||
tags: true,
|
||||
@@ -148,13 +160,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
stack: {
|
||||
assets: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return this.repository.find({
|
||||
where: { id: In(ids) },
|
||||
relations,
|
||||
select,
|
||||
},
|
||||
withDeleted: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -160,6 +160,42 @@ ORDER BY
|
||||
"entity"."localDateTime" DESC
|
||||
|
||||
-- AssetRepository.getByIds
|
||||
SELECT
|
||||
"AssetEntity"."id" AS "AssetEntity_id",
|
||||
"AssetEntity"."deviceAssetId" AS "AssetEntity_deviceAssetId",
|
||||
"AssetEntity"."ownerId" AS "AssetEntity_ownerId",
|
||||
"AssetEntity"."libraryId" AS "AssetEntity_libraryId",
|
||||
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
|
||||
"AssetEntity"."type" AS "AssetEntity_type",
|
||||
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
|
||||
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
|
||||
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
|
||||
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
|
||||
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
|
||||
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
|
||||
"AssetEntity"."updatedAt" AS "AssetEntity_updatedAt",
|
||||
"AssetEntity"."deletedAt" AS "AssetEntity_deletedAt",
|
||||
"AssetEntity"."fileCreatedAt" AS "AssetEntity_fileCreatedAt",
|
||||
"AssetEntity"."localDateTime" AS "AssetEntity_localDateTime",
|
||||
"AssetEntity"."fileModifiedAt" AS "AssetEntity_fileModifiedAt",
|
||||
"AssetEntity"."isFavorite" AS "AssetEntity_isFavorite",
|
||||
"AssetEntity"."isArchived" AS "AssetEntity_isArchived",
|
||||
"AssetEntity"."isExternal" AS "AssetEntity_isExternal",
|
||||
"AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly",
|
||||
"AssetEntity"."isOffline" AS "AssetEntity_isOffline",
|
||||
"AssetEntity"."checksum" AS "AssetEntity_checksum",
|
||||
"AssetEntity"."duration" AS "AssetEntity_duration",
|
||||
"AssetEntity"."isVisible" AS "AssetEntity_isVisible",
|
||||
"AssetEntity"."livePhotoVideoId" AS "AssetEntity_livePhotoVideoId",
|
||||
"AssetEntity"."originalFileName" AS "AssetEntity_originalFileName",
|
||||
"AssetEntity"."sidecarPath" AS "AssetEntity_sidecarPath",
|
||||
"AssetEntity"."stackId" AS "AssetEntity_stackId"
|
||||
FROM
|
||||
"assets" "AssetEntity"
|
||||
WHERE
|
||||
(("AssetEntity"."id" IN ($1)))
|
||||
|
||||
-- AssetRepository.getByIdsWithAllRelations
|
||||
SELECT
|
||||
"AssetEntity"."id" AS "AssetEntity_id",
|
||||
"AssetEntity"."deviceAssetId" AS "AssetEntity_deviceAssetId",
|
||||
|
||||
Reference in New Issue
Block a user