Skip to content

Commit

Permalink
fix(server): query fixes (#15509)
Browse files Browse the repository at this point in the history
  • Loading branch information
mertalev authored Jan 22, 2025
1 parent 7b882b3 commit 49a6961
Show file tree
Hide file tree
Showing 15 changed files with 275 additions and 165 deletions.
64 changes: 16 additions & 48 deletions server/src/entities/asset.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DeduplicateJoinsPlugin, ExpressionBuilder, Kysely, Selectable, SelectQueryBuilder, sql } from 'kysely';
import { DeduplicateJoinsPlugin, ExpressionBuilder, Kysely, SelectQueryBuilder, sql } from 'kysely';
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
import { Assets, DB } from 'src/db';
import { DB } from 'src/db';
import { AlbumEntity } from 'src/entities/album.entity';
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { AssetFileEntity } from 'src/entities/asset-files.entity';
Expand Down Expand Up @@ -181,15 +181,13 @@ export class AssetEntity {
}

export function withExif<O>(qb: SelectQueryBuilder<DB, 'assets', O>) {
return qb
.leftJoin('exif', 'assets.id', 'exif.assetId')
.select((eb) => eb.fn('to_jsonb', [eb.table('exif')]).as('exifInfo'));
return qb.leftJoin('exif', 'assets.id', 'exif.assetId').select((eb) => eb.fn.toJson(eb.table('exif')).as('exifInfo'));
}

export function withExifInner<O>(qb: SelectQueryBuilder<DB, 'assets', O>) {
return qb
.innerJoin('exif', 'assets.id', 'exif.assetId')
.select((eb) => eb.fn('to_jsonb', [eb.table('exif')]).as('exifInfo'));
.select((eb) => eb.fn.toJson(eb.table('exif')).as('exifInfo'));
}

export function withSmartSearch<O>(qb: SelectQueryBuilder<DB, 'assets', O>) {
Expand Down Expand Up @@ -268,48 +266,6 @@ export function withLibrary(eb: ExpressionBuilder<DB, 'assets'>) {
);
}

export function withStackedAssets<O>(qb: SelectQueryBuilder<DB, 'assets' | 'asset_stack', O>) {
return qb
.innerJoinLateral(
(eb: ExpressionBuilder<DB, 'assets' | 'asset_stack'>) =>
eb
.selectFrom('assets as stacked')
.select((eb) => eb.fn<Selectable<Assets>[]>('array_agg', [eb.table('stacked')]).as('assets'))
.whereRef('asset_stack.id', '=', 'stacked.stackId')
.whereRef('asset_stack.primaryAssetId', '!=', 'stacked.id')
.as('s'),
(join) =>
join.on((eb) =>
eb.or([eb('asset_stack.primaryAssetId', '=', eb.ref('assets.id')), eb('assets.stackId', 'is', null)]),
),
)
.select('s.assets');
}

export function withStack<O>(
qb: SelectQueryBuilder<DB, 'assets', O>,
{ assets, count }: { assets: boolean; count: boolean },
) {
return qb
.leftJoinLateral(
(eb) =>
eb
.selectFrom('asset_stack')
.selectAll('asset_stack')
.whereRef('assets.stackId', '=', 'asset_stack.id')
.$if(assets, withStackedAssets)
.$if(count, (qb) =>
// There is no `selectNoFrom` method for expression builders
qb.select(
sql`(select count(*) as "assetCount" where "asset_stack"."id" = "assets"."stackId")`.as('assetCount'),
),
)
.as('stacked_assets'),
(join) => join.onTrue(),
)
.select((eb) => eb.fn('to_jsonb', [eb.table('stacked_assets')]).as('stack'));
}

export function withAlbums<O>(qb: SelectQueryBuilder<DB, 'assets', O>, { albumId }: { albumId?: string }) {
return qb
.select((eb) =>
Expand Down Expand Up @@ -352,6 +308,18 @@ export function truncatedDate<O>(size: TimeBucketSize) {
return sql<O>`date_trunc(${size}, "localDateTime" at time zone 'UTC') at time zone 'UTC'`;
}

export function withTagId<O>(qb: SelectQueryBuilder<DB, 'assets', O>, tagId: string) {
return qb.where((eb) =>
eb.exists(
eb
.selectFrom('tags_closure')
.innerJoin('tag_asset', 'tag_asset.tagsId', 'tags_closure.id_descendant')
.whereRef('tag_asset.assetsId', '=', 'assets.id')
.where('tags_closure.id_ancestor', '=', tagId),
),
);
}

const joinDeduplicationPlugin = new DeduplicateJoinsPlugin();

/** TODO: This should only be used for search-related queries, not as a general purpose query builder */
Expand Down
2 changes: 1 addition & 1 deletion server/src/interfaces/memory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const IMemoryRepository = 'IMemoryRepository';

export interface IMemoryRepository extends IBulkAsset {
search(ownerId: string): Promise<MemoryEntity[]>;
get(id: string): Promise<MemoryEntity | null>;
get(id: string): Promise<MemoryEntity | undefined>;
create(
memory: Omit<Insertable<Memories>, 'data'> & { data: OnThisDayData },
assetIds: Set<string>,
Expand Down
8 changes: 4 additions & 4 deletions server/src/interfaces/person.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Insertable, Updateable } from 'kysely';
import { Insertable, Selectable, Updateable } from 'kysely';
import { AssetFaces, FaceSearch, Person } from 'src/db';
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { PersonEntity } from 'src/entities/person.entity';
Expand Down Expand Up @@ -49,7 +49,7 @@ export interface DeleteFacesOptions {

export type UnassignFacesOptions = DeleteFacesOptions;

export type SelectFaceOptions = Partial<{ [K in keyof AssetFaceEntity]: boolean }>;
export type SelectFaceOptions = (keyof Selectable<AssetFaces>)[];

export interface IPersonRepository {
getAll(options?: Partial<PersonEntity>): AsyncIterableIterator<PersonEntity>;
Expand All @@ -74,10 +74,10 @@ export interface IPersonRepository {
id: string,
relations?: FindOptionsRelations<AssetFaceEntity>,
select?: SelectFaceOptions,
): Promise<AssetFaceEntity | null>;
): Promise<AssetFaceEntity | undefined>;
getFaces(assetId: string): Promise<AssetFaceEntity[]>;
getFacesByIds(ids: AssetFaceId[]): Promise<AssetFaceEntity[]>;
getRandomFace(personId: string): Promise<AssetFaceEntity | null>;
getRandomFace(personId: string): Promise<AssetFaceEntity | undefined>;
getStatistics(personId: string): Promise<PersonStatistics>;
reassignFace(assetFaceId: string, newPersonId: string): Promise<number>;
getNumberOfPeople(userId: string): Promise<PeopleStatistics>;
Expand Down
24 changes: 22 additions & 2 deletions server/src/queries/album.repository.sql
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,31 @@ select
where
"shared_links"."albumId" = "albums"."id"
) as agg
) as "sharedLinks"
) as "sharedLinks",
(
select
json_agg("asset") as "assets"
from
(
select
"assets".*,
to_json("exif") as "exifInfo"
from
"assets"
inner join "exif" on "assets"."id" = "exif"."assetId"
inner join "albums_assets_assets" on "albums_assets_assets"."assetsId" = "assets"."id"
where
"albums_assets_assets"."albumsId" = "albums"."id"
and "assets"."deletedAt" is null
and "assets"."isArchived" = $1
order by
"assets"."fileCreatedAt" desc
) as "asset"
) as "assets"
from
"albums"
where
"albums"."id" = $1
"albums"."id" = $2
and "albums"."deletedAt" is null

-- AlbumRepository.getByAssetId
Expand Down
103 changes: 56 additions & 47 deletions server/src/queries/asset.repository.sql
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ with
)
select
"a".*,
to_jsonb("exif") as "exifInfo"
to_json("exif") as "exifInfo"
from
"today"
inner join lateral (
Expand Down Expand Up @@ -56,7 +56,7 @@ select
(
(now() at time zone 'UTC')::date - ("localDateTime" at time zone 'UTC')::date
) / 365 as "yearsAgo",
jsonb_agg("res") as "assets"
json_agg("res") as "assets"
from
"res"
group by
Expand Down Expand Up @@ -109,34 +109,28 @@ select
"assets"."id" = "tag_asset"."assetsId"
) as agg
) as "tags",
to_jsonb("exif") as "exifInfo",
to_jsonb("stacked_assets") as "stack"
to_json("exif") as "exifInfo",
to_json("stacked_assets") as "stack"
from
"assets"
left join "exif" on "assets"."id" = "exif"."assetId"
left join "asset_stack" on "asset_stack"."id" = "assets"."stackId"
left join lateral (
select
"asset_stack".*,
"s"."assets"
array_agg("stacked") as "assets"
from
"asset_stack"
inner join lateral (
select
array_agg("stacked") as "assets"
from
"assets" as "stacked"
where
"asset_stack"."id" = "stacked"."stackId"
and "asset_stack"."primaryAssetId" != "stacked"."id"
) as "s" on (
"asset_stack"."primaryAssetId" = "assets"."id"
or "assets"."stackId" is null
)
"assets" as "stacked"
where
"assets"."stackId" = "asset_stack"."id"
) as "stacked_assets" on true
"stacked"."stackId" = "asset_stack"."id"
and "stacked"."id" != "asset_stack"."primaryAssetId"
and "stacked"."deletedAt" is null
and "stacked"."isArchived" = $1
group by
"asset_stack"."id"
) as "stacked_assets" on "asset_stack"."id" is not null
where
"assets"."id" = any ($1::uuid [])
"assets"."id" = any ($2::uuid [])

-- AssetRepository.deleteAll
delete from "assets"
Expand Down Expand Up @@ -278,14 +272,33 @@ order by
-- AssetRepository.getTimeBucket
select
"assets".*,
to_jsonb("exif") as "exifInfo"
to_json("exif") as "exifInfo",
to_json("stacked_assets") as "stack"
from
"assets"
left join "exif" on "assets"."id" = "exif"."assetId"
left join "asset_stack" on "asset_stack"."id" = "assets"."stackId"
left join lateral (
select
"asset_stack".*,
count("stacked") as "assetCount"
from
"assets" as "stacked"
where
"stacked"."stackId" = "asset_stack"."id"
and "stacked"."deletedAt" is null
and "stacked"."isArchived" = $1
group by
"asset_stack"."id"
) as "stacked_assets" on "asset_stack"."id" is not null
where
"assets"."deletedAt" is null
and "assets"."isVisible" = $1
and date_trunc($2, "localDateTime" at time zone 'UTC') at time zone 'UTC' = $3
(
"asset_stack"."primaryAssetId" = "assets"."id"
or "assets"."stackId" is null
)
and "assets"."deletedAt" is null
and "assets"."isVisible" = $2
and date_trunc($3, "localDateTime" at time zone 'UTC') at time zone 'UTC' = $4
order by
"assets"."localDateTime" desc

Expand Down Expand Up @@ -368,25 +381,23 @@ limit
-- AssetRepository.getAllForUserFullSync
select
"assets".*,
to_jsonb("exif") as "exifInfo",
to_jsonb("stacked_assets") as "stack"
to_json("exif") as "exifInfo",
to_json("stacked_assets") as "stack"
from
"assets"
left join "exif" on "assets"."id" = "exif"."assetId"
left join "asset_stack" on "asset_stack"."id" = "assets"."stackId"
left join lateral (
select
"asset_stack".*,
(
select
count(*) as "assetCount"
where
"asset_stack"."id" = "assets"."stackId"
) as "assetCount"
count("stacked") as "assetCount"
from
"asset_stack"
"assets" as "stacked"
where
"assets"."stackId" = "asset_stack"."id"
) as "stacked_assets" on true
"stacked"."stackId" = "asset_stack"."id"
group by
"asset_stack"."id"
) as "stacked_assets" on "asset_stack"."id" is not null
where
"assets"."ownerId" = $1::uuid
and "isVisible" = $2
Expand All @@ -400,25 +411,23 @@ limit
-- AssetRepository.getChangedDeltaSync
select
"assets".*,
to_jsonb("exif") as "exifInfo",
to_jsonb("stacked_assets") as "stack"
to_json("exif") as "exifInfo",
to_json("stacked_assets") as "stack"
from
"assets"
left join "exif" on "assets"."id" = "exif"."assetId"
left join "asset_stack" on "asset_stack"."id" = "assets"."stackId"
left join lateral (
select
"asset_stack".*,
(
select
count(*) as "assetCount"
where
"asset_stack"."id" = "assets"."stackId"
) as "assetCount"
count("stacked") as "assetCount"
from
"asset_stack"
"assets" as "stacked"
where
"assets"."stackId" = "asset_stack"."id"
) as "stacked_assets" on true
"stacked"."stackId" = "asset_stack"."id"
group by
"asset_stack"."id"
) as "stacked_assets" on "asset_stack"."id" is not null
where
"assets"."ownerId" = any ($1::uuid [])
and "isVisible" = $2
Expand Down
13 changes: 11 additions & 2 deletions server/src/queries/library.repository.sql
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ order by

-- LibraryRepository.getStatistics
select
count("assets"."id") filter (
count(*) filter (
where
(
"assets"."type" = $1
Expand All @@ -130,8 +130,17 @@ select
from
"libraries"
inner join "assets" on "assets"."libraryId" = "libraries"."id"
inner join "exif" on "exif"."assetId" = "assets"."id"
left join "exif" on "exif"."assetId" = "assets"."id"
where
"libraries"."id" = $6
group by
"libraries"."id"
select
0::int as "photos",
0::int as "videos",
0::int as "usage",
0::int as "total"
from
"libraries"
where
"libraries"."id" = $1
2 changes: 1 addition & 1 deletion server/src/queries/view.repository.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ where
-- ViewRepository.getAssetsByOriginalPath
select
"assets".*,
to_jsonb("exif") as "exifInfo"
to_json("exif") as "exifInfo"
from
"assets"
left join "exif" on "assets"."id" = "exif"."assetId"
Expand Down
Loading

0 comments on commit 49a6961

Please sign in to comment.