mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat(server): Merge Faces sorted by Similarity (#14635)
* Merge Faces sorted by Similarity * Adds face sorting to the side panel face merger * run make open-api * Make it one query * Only have the single order by when sorting by closest face
This commit is contained in:
parent
8945a5d862
commit
12e55f5bf0
11 changed files with 136 additions and 44 deletions
|
|
@ -31,8 +31,8 @@ export class PersonController {
|
|||
|
||||
@Get()
|
||||
@Authenticated({ permission: Permission.PERSON_READ })
|
||||
getAllPeople(@Auth() auth: AuthDto, @Query() withHidden: PersonSearchDto): Promise<PeopleResponseDto> {
|
||||
return this.service.getAll(auth, withHidden);
|
||||
getAllPeople(@Auth() auth: AuthDto, @Query() options: PersonSearchDto): Promise<PeopleResponseDto> {
|
||||
return this.service.getAll(auth, options);
|
||||
}
|
||||
|
||||
@Post()
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ export class MergePersonDto {
|
|||
export class PersonSearchDto {
|
||||
@ValidateBoolean({ optional: true })
|
||||
withHidden?: boolean;
|
||||
@ValidateUUID({ optional: true })
|
||||
closestPersonId?: string;
|
||||
@ValidateUUID({ optional: true })
|
||||
closestAssetId?: string;
|
||||
|
||||
/** Page number for pagination */
|
||||
@ApiPropertyOptional()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export const IPersonRepository = 'IPersonRepository';
|
|||
export interface PersonSearchOptions {
|
||||
minimumFaceCount: number;
|
||||
withHidden: boolean;
|
||||
closestFaceAssetId?: string;
|
||||
}
|
||||
|
||||
export interface PersonNameSearchOptions {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,11 @@ export class PersonRepository implements IPersonRepository {
|
|||
}
|
||||
|
||||
@GenerateSql({ params: [{ take: 10, skip: 10 }, DummyValue.UUID] })
|
||||
getAllForUser(pagination: PaginationOptions, userId: string, options?: PersonSearchOptions): Paginated<PersonEntity> {
|
||||
async getAllForUser(
|
||||
pagination: PaginationOptions,
|
||||
userId: string,
|
||||
options?: PersonSearchOptions,
|
||||
): Paginated<PersonEntity> {
|
||||
const queryBuilder = this.personRepository
|
||||
.createQueryBuilder('person')
|
||||
.innerJoin('person.faces', 'face')
|
||||
|
|
@ -97,10 +101,22 @@ export class PersonRepository implements IPersonRepository {
|
|||
.addOrderBy('person.createdAt')
|
||||
.having("person.name != '' OR COUNT(face.assetId) >= :faces", { faces: options?.minimumFaceCount || 1 })
|
||||
.groupBy('person.id');
|
||||
if (options?.closestFaceAssetId) {
|
||||
const innerQueryBuilder = this.faceSearchRepository
|
||||
.createQueryBuilder('face_search')
|
||||
.select('embedding', 'embedding')
|
||||
.where('"face_search"."faceId" = "person"."faceAssetId"');
|
||||
const faceSelectQueryBuilder = this.faceSearchRepository
|
||||
.createQueryBuilder('face_search')
|
||||
.select('embedding', 'embedding')
|
||||
.where('"face_search"."faceId" = :faceId', { faceId: options.closestFaceAssetId });
|
||||
queryBuilder
|
||||
.orderBy('(' + innerQueryBuilder.getQuery() + ') <=> (' + faceSelectQueryBuilder.getQuery() + ')')
|
||||
.setParameters(faceSelectQueryBuilder.getParameters());
|
||||
}
|
||||
if (!options?.withHidden) {
|
||||
queryBuilder.andWhere('person.isHidden = false');
|
||||
}
|
||||
|
||||
return paginatedBuilder(queryBuilder, {
|
||||
mode: PaginationMode.LIMIT_OFFSET,
|
||||
...pagination,
|
||||
|
|
|
|||
|
|
@ -55,16 +55,25 @@ import { IsNull } from 'typeorm';
|
|||
@Injectable()
|
||||
export class PersonService extends BaseService {
|
||||
async getAll(auth: AuthDto, dto: PersonSearchDto): Promise<PeopleResponseDto> {
|
||||
const { withHidden = false, page, size } = dto;
|
||||
const { withHidden = false, closestAssetId, closestPersonId, page, size } = dto;
|
||||
let closestFaceAssetId = closestAssetId;
|
||||
const pagination = {
|
||||
take: size,
|
||||
skip: (page - 1) * size,
|
||||
};
|
||||
|
||||
if (closestPersonId) {
|
||||
const person = await this.personRepository.getById(closestPersonId);
|
||||
if (!person?.faceAssetId) {
|
||||
throw new NotFoundException('Person not found');
|
||||
}
|
||||
closestFaceAssetId = person.faceAssetId;
|
||||
}
|
||||
const { machineLearning } = await this.getConfig({ withCache: false });
|
||||
const { items, hasNextPage } = await this.personRepository.getAllForUser(pagination, auth.user.id, {
|
||||
minimumFaceCount: machineLearning.facialRecognition.minFaces,
|
||||
withHidden,
|
||||
closestFaceAssetId,
|
||||
});
|
||||
const { total, hidden } = await this.personRepository.getNumberOfPeople(auth.user.id);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue