Skip to content

Commit

Permalink
Use tagId option
Browse files Browse the repository at this point in the history
fix time bucket stack relation

fix library statistics

fix empty array updates

more fixes

early return for `getFacesByIds`

remove unused import

update types

extract tag id fragment to helper method

generate sql

inline `withStack`
  • Loading branch information
mertalev committed Jan 22, 2025
1 parent 8d6cbb5 commit e49cb70
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 e49cb70

Please sign in to comment.