pull/27336/merge
Hugo Pereira 2026-06-03 08:18:36 -05:00 committed by GitHub
commit 1b20f4b0ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 55 additions and 21 deletions

View File

@ -218,11 +218,29 @@ from
"person"
where
"person"."ownerId" = $1
and f_unaccent ("person"."name") %> f_unaccent ($2)
and exists (
select
from
"asset_face"
where
"asset_face"."personId" = "person"."id"
and "asset_face"."deletedAt" is null
and "asset_face"."isVisible" = $2
and exists (
select
from
"asset"
where
"asset"."id" = "asset_face"."assetId"
and "asset"."visibility" = 'timeline'
and "asset"."deletedAt" is null
)
)
and f_unaccent ("person"."name") %> f_unaccent ($3)
order by
f_unaccent ("person"."name") <->>> f_unaccent ($3)
f_unaccent ("person"."name") <->>> f_unaccent ($4)
limit
$4
$5
-- PersonRepository.getDistinctNames
select distinct

View File

@ -12,6 +12,25 @@ import { PersonTable } from 'src/schema/tables/person.table';
import { dummy, removeUndefinedKeys, withFilePath } from 'src/utils/database';
import { paginationHelper, PaginationOptions } from 'src/utils/pagination';
function hasFace(eb: ExpressionBuilder<DB, 'person'>) {
return eb.exists((eb) =>
eb
.selectFrom('asset_face')
.whereRef('asset_face.personId', '=', 'person.id')
.where('asset_face.deletedAt', 'is', null)
.where('asset_face.isVisible', '=', true)
.where((eb) =>
eb.exists((eb) =>
eb
.selectFrom('asset')
.whereRef('asset.id', '=', 'asset_face.assetId')
.where('asset.visibility', '=', sql.lit(AssetVisibility.Timeline))
.where('asset.deletedAt', 'is', null),
),
),
);
}
export interface PersonSearchOptions {
withHidden: boolean;
closestFaceAssetId?: string;
@ -325,6 +344,7 @@ export class PersonRepository {
.selectFrom(['similarity_threshold', 'person'])
.selectAll('person')
.where('person.ownerId', '=', userId)
.where(hasFace)
.where(() => sql`f_unaccent("person"."name") %> f_unaccent(${personName})`)
.orderBy(sql`f_unaccent("person"."name") <->>> f_unaccent(${personName})`)
.limit(100)
@ -369,24 +389,7 @@ export class PersonRepository {
const zero = sql.lit(0);
return this.db
.selectFrom('person')
.where((eb) =>
eb.exists((eb) =>
eb
.selectFrom('asset_face')
.whereRef('asset_face.personId', '=', 'person.id')
.where('asset_face.deletedAt', 'is', null)
.where('asset_face.isVisible', '=', true)
.where((eb) =>
eb.exists((eb) =>
eb
.selectFrom('asset')
.whereRef('asset.id', '=', 'asset_face.assetId')
.where('asset.visibility', '=', sql.lit(AssetVisibility.Timeline))
.where('asset.deletedAt', 'is', null),
),
),
),
)
.where(hasFace)
.where('person.ownerId', '=', userId)
.select((eb) => eb.fn.coalesce(eb.fn.countAll<number>(), zero).as('total'))
.select((eb) => eb.fn.coalesce(eb.fn.countAll<number>().filterWhere('isHidden', '=', true), zero).as('hidden'))

View File

@ -4,6 +4,7 @@ import { SearchSuggestionType } from 'src/dtos/search.dto';
import { SearchService } from 'src/services/search.service';
import { AssetFactory } from 'test/factories/asset.factory';
import { AuthFactory } from 'test/factories/auth.factory';
import { PersonFactory } from 'test/factories/person.factory';
import { authStub } from 'test/fixtures/auth.stub';
import { getForAsset } from 'test/mappers';
import { newTestService, ServiceMocks } from 'test/utils';
@ -39,6 +40,18 @@ describe(SearchService.name, () => {
expect(mocks.person.getByName).toHaveBeenCalledWith(auth.user.id, name, { withHidden: true });
});
it('should exclude people without faces', async () => {
const auth = AuthFactory.create();
const withFace = PersonFactory.create({ ownerId: auth.user.id, faceAssetId: 'face-id', name: 'Alice' });
const withoutFace = PersonFactory.create({ ownerId: auth.user.id, faceAssetId: null, name: 'Alina' });
mocks.person.getByName.mockResolvedValue([withFace, withoutFace]);
const result = await sut.searchPerson(auth, { name: 'Ali', withHidden: false });
expect(result).toEqual([expect.objectContaining({ id: withFace.id, name: withFace.name })]);
});
});
describe('searchPlaces', () => {