This commit is contained in:
Laurens 2026-08-14 08:24:12 -05:00 committed by GitHub
commit 7e5927e639
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 103 additions and 1 deletions

View file

@ -10,6 +10,30 @@ select
(
select
"asset".*,
(
select
coalesce(json_agg(agg), '[]')
from
(
select
"asset_face".*,
"person" as "person"
from
"asset_face"
left join lateral (
select
"person".*
from
"person"
where
"asset_face"."personId" = "person"."id"
) as "person" on true
where
"asset_face"."assetId" = "asset"."id"
and "asset_face"."deletedAt" is null
and "asset_face"."isVisible" is true
) as agg
) as "faces",
to_json("exifInfo") as "exifInfo"
from
"asset"
@ -76,6 +100,30 @@ select
(
select
"asset".*,
(
select
coalesce(json_agg(agg), '[]')
from
(
select
"asset_face".*,
"person" as "person"
from
"asset_face"
left join lateral (
select
"person".*
from
"person"
where
"asset_face"."personId" = "person"."id"
) as "person" on true
where
"asset_face"."assetId" = "asset"."id"
and "asset_face"."deletedAt" is null
and "asset_face"."isVisible" is true
) as agg
) as "faces",
(
select
coalesce(json_agg(agg), '[]')

View file

@ -6,7 +6,7 @@ import { columns } from 'src/database';
import { DummyValue, GenerateSql } from 'src/decorators';
import { DB } from 'src/schema';
import { StackTable } from 'src/schema/tables/stack.table';
import { asUuid, withDefaultVisibility } from 'src/utils/database';
import { asUuid, withDefaultVisibility, withFacesAndPeople } from 'src/utils/database';
export interface StackSearch {
ownerId: string;
@ -27,6 +27,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'stack'>, withTags = false) => {
.as('exifInfo'),
(join) => join.onTrue(),
)
.select(withFacesAndPeople)
.$if(withTags, (eb) =>
eb.select((eb) =>
jsonArrayFrom(

View file

@ -203,6 +203,12 @@ export const getForStack = (stack: ReturnType<StackFactory['build']>) => ({
assets: stack.assets.map((asset) => ({
...getDehydrated(asset),
exifInfo: getDehydrated(asset.exifInfo),
faces: asset.faces
.filter((face): face is typeof face & { person: NonNullable<typeof face.person> } => face.person !== null)
.map((face) => ({
...getDehydrated(face),
person: getDehydrated(face.person),
})),
})),
});

View file

@ -0,0 +1,47 @@
import { Kysely } from 'kysely';
import { LoggingRepository } from 'src/repositories/logging.repository';
import { StackRepository } from 'src/repositories/stack.repository';
import { DB } from 'src/schema';
import { BaseService } from 'src/services/base.service';
import { newMediumService } from 'test/medium.factory';
import { getKyselyDB } from 'test/utils';
let defaultDatabase: Kysely<DB>;
const setup = (db?: Kysely<DB>) => {
const { ctx } = newMediumService(BaseService, {
database: db || defaultDatabase,
real: [],
mock: [LoggingRepository],
});
return { ctx, sut: ctx.get(StackRepository) };
};
beforeAll(async () => {
defaultDatabase = await getKyselyDB();
});
describe(StackRepository.name, () => {
describe('getById', () => {
it('should return faces for stack assets', async () => {
const { ctx, sut } = setup();
const { user } = await ctx.newUser();
const { asset: primaryAsset } = await ctx.newAsset({ ownerId: user.id });
const { asset: secondaryAsset } = await ctx.newAsset({ ownerId: user.id });
await ctx.newExif({ assetId: primaryAsset.id, fileSizeInByte: 123 });
await ctx.newExif({ assetId: secondaryAsset.id, fileSizeInByte: 456 });
const { person } = await ctx.newPerson({ ownerId: user.id });
await ctx.newAssetFace({ assetId: primaryAsset.id, personId: person.id });
const { result: createdStack } = await ctx.newStack({ ownerId: user.id }, [primaryAsset.id, secondaryAsset.id]);
const stack = await sut.getById(createdStack.id);
expect(stack).toBeDefined();
expect(stack?.assets[0]?.faces).toHaveLength(1);
expect(stack?.assets[0]?.faces[0]?.person?.id).toBe(person.id);
});
});
});