feat: improve performance for GET /api/album & /api/album/:id (#17124)

* fix(server) optimize number of sql calls for GET /api/albums

remove unnecessary join for getMetadataForIds
remove separate call to getLastUpdatedAssetForAlbumId

* fix(server) remove unnecessary getLastUpdatedAssetForAlbumId call for GET /api/album/:id

also remove getLastUpdatedAssetForAlbumId query as it is no longer referenced

* fix(server): correct lastModifiedAssetTimestamp return type + formatting and typing

* chore(server): address type issue with tests found via npm:check

tests & lint still pass before this commit.
This commit is contained in:
PathToLife
2025-04-01 00:28:41 +13:00
committed by GitHub
parent 238c151ac3
commit 09f4476f97
6 changed files with 71 additions and 51 deletions

View File

@@ -41,8 +41,20 @@ describe(AlbumService.name, () => {
it('gets list of albums for auth user', async () => {
mocks.album.getOwned.mockResolvedValue([albumStub.empty, albumStub.sharedWithUser]);
mocks.album.getMetadataForIds.mockResolvedValue([
{ albumId: albumStub.empty.id, assetCount: 0, startDate: null, endDate: null },
{ albumId: albumStub.sharedWithUser.id, assetCount: 0, startDate: null, endDate: null },
{
albumId: albumStub.empty.id,
assetCount: 0,
startDate: null,
endDate: null,
lastModifiedAssetTimestamp: null,
},
{
albumId: albumStub.sharedWithUser.id,
assetCount: 0,
startDate: null,
endDate: null,
lastModifiedAssetTimestamp: null,
},
]);
const result = await sut.getAll(authStub.admin, {});
@@ -59,6 +71,7 @@ describe(AlbumService.name, () => {
assetCount: 1,
startDate: new Date('1970-01-01'),
endDate: new Date('1970-01-01'),
lastModifiedAssetTimestamp: new Date('1970-01-01'),
},
]);
@@ -71,7 +84,13 @@ describe(AlbumService.name, () => {
it('gets list of albums that are shared', async () => {
mocks.album.getShared.mockResolvedValue([albumStub.sharedWithUser]);
mocks.album.getMetadataForIds.mockResolvedValue([
{ albumId: albumStub.sharedWithUser.id, assetCount: 0, startDate: null, endDate: null },
{
albumId: albumStub.sharedWithUser.id,
assetCount: 0,
startDate: null,
endDate: null,
lastModifiedAssetTimestamp: null,
},
]);
const result = await sut.getAll(authStub.admin, { shared: true });
@@ -83,7 +102,13 @@ describe(AlbumService.name, () => {
it('gets list of albums that are NOT shared', async () => {
mocks.album.getNotShared.mockResolvedValue([albumStub.empty]);
mocks.album.getMetadataForIds.mockResolvedValue([
{ albumId: albumStub.empty.id, assetCount: 0, startDate: null, endDate: null },
{
albumId: albumStub.empty.id,
assetCount: 0,
startDate: null,
endDate: null,
lastModifiedAssetTimestamp: null,
},
]);
const result = await sut.getAll(authStub.admin, { shared: false });
@@ -101,6 +126,7 @@ describe(AlbumService.name, () => {
assetCount: 1,
startDate: new Date('1970-01-01'),
endDate: new Date('1970-01-01'),
lastModifiedAssetTimestamp: new Date('1970-01-01'),
},
]);
@@ -447,6 +473,7 @@ describe(AlbumService.name, () => {
assetCount: 1,
startDate: new Date('1970-01-01'),
endDate: new Date('1970-01-01'),
lastModifiedAssetTimestamp: new Date('1970-01-01'),
},
]);
@@ -468,6 +495,7 @@ describe(AlbumService.name, () => {
assetCount: 1,
startDate: new Date('1970-01-01'),
endDate: new Date('1970-01-01'),
lastModifiedAssetTimestamp: new Date('1970-01-01'),
},
]);
@@ -489,6 +517,7 @@ describe(AlbumService.name, () => {
assetCount: 1,
startDate: new Date('1970-01-01'),
endDate: new Date('1970-01-01'),
lastModifiedAssetTimestamp: new Date('1970-01-01'),
},
]);

View File

@@ -58,19 +58,15 @@ export class AlbumService extends BaseService {
albumMetadata[metadata.albumId] = metadata;
}
return Promise.all(
albums.map(async (album) => {
const lastModifiedAsset = await this.assetRepository.getLastUpdatedAssetForAlbumId(album.id);
return {
...mapAlbumWithoutAssets(album),
sharedLinks: undefined,
startDate: albumMetadata[album.id]?.startDate ?? undefined,
endDate: albumMetadata[album.id]?.endDate ?? undefined,
assetCount: albumMetadata[album.id]?.assetCount ?? 0,
lastModifiedAssetTimestamp: lastModifiedAsset?.updatedAt,
};
}),
);
return albums.map((album) => ({
...mapAlbumWithoutAssets(album),
sharedLinks: undefined,
startDate: albumMetadata[album.id]?.startDate ?? undefined,
endDate: albumMetadata[album.id]?.endDate ?? undefined,
assetCount: albumMetadata[album.id]?.assetCount ?? 0,
// lastModifiedAssetTimestamp is only used in mobile app, please remove if not need
lastModifiedAssetTimestamp: albumMetadata[album.id]?.lastModifiedAssetTimestamp ?? undefined,
}));
}
async get(auth: AuthDto, id: string, dto: AlbumInfoDto): Promise<AlbumResponseDto> {
@@ -79,14 +75,13 @@ export class AlbumService extends BaseService {
const withAssets = dto.withoutAssets === undefined ? true : !dto.withoutAssets;
const album = await this.findOrFail(id, { withAssets });
const [albumMetadataForIds] = await this.albumRepository.getMetadataForIds([album.id]);
const lastModifiedAsset = await this.assetRepository.getLastUpdatedAssetForAlbumId(album.id);
return {
...mapAlbum(album, withAssets, auth),
startDate: albumMetadataForIds?.startDate ?? undefined,
endDate: albumMetadataForIds?.endDate ?? undefined,
assetCount: albumMetadataForIds?.assetCount ?? 0,
lastModifiedAssetTimestamp: lastModifiedAsset?.updatedAt,
lastModifiedAssetTimestamp: albumMetadataForIds?.lastModifiedAssetTimestamp ?? undefined,
};
}