diff --git a/server/src/queries/person.repository.sql b/server/src/queries/person.repository.sql index 7eb7c3bd1..9218f6a79 100644 --- a/server/src/queries/person.repository.sql +++ b/server/src/queries/person.repository.sql @@ -239,38 +239,52 @@ from "person_group" inner join "person" as "owned" on "owned"."personGroupId" = "person_group"."id" and "owned"."ownerId" = $5 - inner join "asset_face" on "asset_face"."personGroupId" = "person_group"."id" - and "asset_face"."deletedAt" is null - and "asset_face"."isVisible" is true - inner join "asset" on "asset"."id" = "asset_face"."assetId" - and "asset"."visibility" = 'timeline' - and "asset"."deletedAt" is null - and ( - "asset"."ownerId" = any ($6::uuid[]) - or exists ( - select - 1 as "exists" - from - "album_asset" - inner join "album" on "album"."id" = "album_asset"."albumId" - and "album"."deletedAt" is null - inner join "album_user" on "album_user"."albumId" = "album"."id" - and "album_user"."userId" = $7::uuid - where - "album_asset"."assetId" = "asset"."id" - ) - ) + inner join ( + select + "asset_face"."personGroupId", + count("asset"."id") as "faceCount" + from + "asset_face" + inner join "asset" on "asset"."id" = "asset_face"."assetId" + and "asset"."visibility" = 'timeline' + and "asset"."deletedAt" is null + and ( + "asset"."ownerId" = any ($6::uuid[]) + or exists ( + select + 1 as "exists" + from + "album_asset" + inner join "album" on "album"."id" = "album_asset"."albumId" + and "album"."deletedAt" is null + inner join "album_user" on "album_user"."albumId" = "album"."id" + and "album_user"."userId" = $7::uuid + where + "album_asset"."assetId" = "asset"."id" + ) + ) + where + "asset_face"."personGroupId" = any ( + array( + ( + select + "person"."personGroupId" + from + "person" + where + "person"."ownerId" = $8 + ) + ) + ) + and "asset_face"."deletedAt" is null + and "asset_face"."isVisible" is true + group by + "asset_face"."personGroupId" + ) as "faces" on "faces"."personGroupId" = "person_group"."id" where - "owned"."isHidden" = $8 - and 1 = 1 -group by - "person_group"."id", - "owned"."ownerId", - "owned"."personGroupId" -having ( "owned"."name" != $9 - or count("asset"."id") >= COALESCE( + or "faces"."faceCount" >= COALESCE( ( SELECT value -> 'people' ->> 'minimumFaces' @@ -283,17 +297,19 @@ having '3' )::int ) + and "owned"."isHidden" = $11 + and 1 = 1 order by "owned"."isHidden" asc, "owned"."isFavorite" desc, NULLIF("owned"."name", '') is null asc, - count("asset"."id") desc, + "faces"."faceCount" desc, NULLIF("owned"."name", '') asc nulls last, "owned"."createdAt" limit - $11 -offset $12 +offset + $13 -- PersonRepository.getAllWithoutFaces select @@ -901,49 +917,65 @@ from "person_group" inner join "person" as "owned" on "owned"."personGroupId" = "person_group"."id" and "owned"."ownerId" = $2 - inner join "asset_face" on "asset_face"."personGroupId" = "person_group"."id" - and "asset_face"."deletedAt" is null - and "asset_face"."isVisible" is true - inner join "asset" on "asset"."id" = "asset_face"."assetId" - and "asset"."visibility" = 'timeline' - and "asset"."deletedAt" is null - and ( - "asset"."ownerId" = any ($3::uuid[]) - or exists ( - select - 1 as "exists" - from - "album_asset" - inner join "album" on "album"."id" = "album_asset"."albumId" - and "album"."deletedAt" is null - inner join "album_user" on "album_user"."albumId" = "album"."id" - and "album_user"."userId" = $4::uuid - where - "album_asset"."assetId" = "asset"."id" - ) - ) + inner join ( + select + "asset_face"."personGroupId", + count("asset"."id") as "faceCount" + from + "asset_face" + inner join "asset" on "asset"."id" = "asset_face"."assetId" + and "asset"."visibility" = 'timeline' + and "asset"."deletedAt" is null + and ( + "asset"."ownerId" = any ($3::uuid[]) + or exists ( + select + 1 as "exists" + from + "album_asset" + inner join "album" on "album"."id" = "album_asset"."albumId" + and "album"."deletedAt" is null + inner join "album_user" on "album_user"."albumId" = "album"."id" + and "album_user"."userId" = $4::uuid + where + "album_asset"."assetId" = "asset"."id" + ) + ) + where + "asset_face"."personGroupId" = any ( + array( + ( + select + "person"."personGroupId" + from + "person" + where + "person"."ownerId" = $5 + ) + ) + ) + and "asset_face"."deletedAt" is null + and "asset_face"."isVisible" is true + group by + "asset_face"."personGroupId" + ) as "faces" on "faces"."personGroupId" = "person_group"."id" where - 1 = 1 - group by - "person_group"."id", - "owned"."ownerId", - "owned"."personGroupId" - having ( - "owned"."name" != $5 - or count("asset"."id") >= COALESCE( + "owned"."name" != $6 + or "faces"."faceCount" >= COALESCE( ( SELECT value -> 'people' ->> 'minimumFaces' FROM user_metadata WHERE - "userId" = $6 + "userId" = $7 AND key = 'preferences' ), '3' )::int ) + and 1 = 1 ) as "people" -- PersonRepository.createGroup diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 9b9235b8b..d6378d6ec 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -224,8 +224,6 @@ const asPerson = ({ ownedPerson, otherPeople, sharedBy, sharedWith }: PersonGrou sharedWith, }); -const faceCount = (eb: ExpressionBuilder) => eb.fn.count('asset.id'); - const withPerson = ({ viewingUserId }: WithPersonOptions) => { return (eb: ExpressionBuilder) => jsonObjectFrom( @@ -448,25 +446,40 @@ export class PersonRepository { .innerJoin('person as owned', (join) => join.onRef('owned.personGroupId', '=', 'person_group.id').on('owned.ownerId', '=', userId), ) - .innerJoin('asset_face', (join) => - join - .onRef('asset_face.personGroupId', '=', 'person_group.id') - .on('asset_face.deletedAt', 'is', null) - .on('asset_face.isVisible', 'is', true), + .innerJoin( + (eb) => + eb + .selectFrom('asset_face') + .innerJoin('asset', (join) => + join + .onRef('asset.id', '=', 'asset_face.assetId') + .on('asset.visibility', '=', sql.lit(AssetVisibility.Timeline)) + .on('asset.deletedAt', 'is', null) + .on((eb) => + eb.or([eb('asset.ownerId', '=', anyUuid([userId, ...partnerIds])), inSharedAlbum(eb, userId)]), + ), + ) + .select('asset_face.personGroupId') + .select((eb) => eb.fn.count('asset.id').as('faceCount')) + // an array instead of a join or `in`, which the planner may turn into a scan of every face in the instance + .where('asset_face.personGroupId', '=', (eb) => { + const ownedGroupIds = eb + .selectFrom('person') + .select('person.personGroupId') + .where('person.ownerId', '=', userId); + return eb.fn.any(eb.fn('array', [ownedGroupIds])); + }) + .where('asset_face.deletedAt', 'is', null) + .where('asset_face.isVisible', 'is', true) + .groupBy('asset_face.personGroupId') + .as('faces'), + (join) => join.onRef('faces.personGroupId', '=', 'person_group.id'), ) - .innerJoin('asset', (join) => - join - .onRef('asset.id', '=', 'asset_face.assetId') - .on('asset.visibility', '=', sql.lit(AssetVisibility.Timeline)) - .on('asset.deletedAt', 'is', null) - .on((eb) => eb.or([eb('asset.ownerId', '=', anyUuid([userId, ...partnerIds])), inSharedAlbum(eb, userId)])), - ) - .groupBy(['person_group.id', 'owned.ownerId', 'owned.personGroupId']) - .having((eb) => + .where((eb) => eb.or([ eb('owned.name', '!=', ''), eb( - faceCount, + 'faces.faceCount', '>=', sql`COALESCE( (SELECT value -> 'people' ->> 'minimumFaces' @@ -509,7 +522,7 @@ export class PersonRepository { .$if(!options.closestFaceAssetId, (qb) => qb .orderBy(sql`NULLIF("owned"."name", '') is null`, 'asc') - .orderBy(faceCount, 'desc') + .orderBy('faces.faceCount', 'desc') .orderBy(sql`NULLIF("owned"."name", '')`, (om) => om.asc().nullsLast()) .orderBy('owned.createdAt'), )