mirror of
https://github.com/KevinMidboe/immich.git
synced 2025-10-29 17:40:28 +00:00
feat(server,web): improve performances in person page (1) (#4387)
* feat: improve performances in people page * feat: add loadingspinner when searching * fix: reset people on error * fix: case insensitive * feat: better sql query * fix: reset people list before api request * fix: format
This commit is contained in:
@@ -3789,6 +3789,50 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/search/person": {
|
||||
"get": {
|
||||
"operationId": "searchPerson",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PersonResponseDto"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Search"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/server-info": {
|
||||
"get": {
|
||||
"operationId": "getServerInfo",
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface IPersonRepository {
|
||||
getAllForUser(userId: string, options: PersonSearchOptions): Promise<PersonEntity[]>;
|
||||
getAllWithoutFaces(): Promise<PersonEntity[]>;
|
||||
getById(personId: string): Promise<PersonEntity | null>;
|
||||
getByName(userId: string, personName: string): Promise<PersonEntity[]>;
|
||||
|
||||
getAssets(personId: string): Promise<AssetEntity[]>;
|
||||
prepareReassignFaces(data: UpdateFacesData): Promise<string[]>;
|
||||
|
||||
@@ -85,3 +85,9 @@ export class SearchDto {
|
||||
@Transform(toBoolean)
|
||||
motion?: boolean;
|
||||
}
|
||||
|
||||
export class SearchPeopleDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name!: string;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { AssetResponseDto, mapAsset } from '../asset';
|
||||
import { AuthUserDto } from '../auth';
|
||||
import { usePagination } from '../domain.util';
|
||||
import { IAssetFaceJob, IBulkEntityJob, JOBS_ASSET_PAGINATION_SIZE, JobName } from '../job';
|
||||
import { PersonResponseDto } from '../person/person.dto';
|
||||
import {
|
||||
AssetFaceId,
|
||||
IAlbumRepository,
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
SearchStrategy,
|
||||
} from '../repositories';
|
||||
import { FeatureFlag, SystemConfigCore } from '../system-config';
|
||||
import { SearchDto } from './dto';
|
||||
import { SearchDto, SearchPeopleDto } from './dto';
|
||||
import { SearchResponseDto } from './response-dto';
|
||||
|
||||
interface SyncQueue {
|
||||
@@ -158,6 +159,10 @@ export class SearchService {
|
||||
};
|
||||
}
|
||||
|
||||
async searchPerson(authUser: AuthUserDto, dto: SearchPeopleDto): Promise<PersonResponseDto[]> {
|
||||
return await this.personRepository.getByName(authUser.id, dto.name);
|
||||
}
|
||||
|
||||
async handleIndexAlbums() {
|
||||
if (!this.enabled) {
|
||||
return false;
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { AuthUserDto, SearchDto, SearchExploreResponseDto, SearchResponseDto, SearchService } from '@app/domain';
|
||||
import {
|
||||
AuthUserDto,
|
||||
PersonResponseDto,
|
||||
SearchDto,
|
||||
SearchExploreResponseDto,
|
||||
SearchPeopleDto,
|
||||
SearchResponseDto,
|
||||
SearchService,
|
||||
} from '@app/domain';
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AuthUser, Authenticated } from '../app.guard';
|
||||
@@ -11,6 +19,11 @@ import { UseValidation } from '../app.utils';
|
||||
export class SearchController {
|
||||
constructor(private service: SearchService) {}
|
||||
|
||||
@Get('person')
|
||||
searchPerson(@AuthUser() authUser: AuthUserDto, @Query() dto: SearchPeopleDto): Promise<PersonResponseDto[]> {
|
||||
return this.service.searchPerson(authUser, dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
search(@AuthUser() authUser: AuthUserDto, @Query() dto: SearchDto): Promise<SearchResponseDto> {
|
||||
return this.service.search(authUser, dto);
|
||||
|
||||
@@ -95,6 +95,16 @@ export class PersonRepository implements IPersonRepository {
|
||||
return this.personRepository.findOne({ where: { id: personId } });
|
||||
}
|
||||
|
||||
getByName(userId: string, personName: string): Promise<PersonEntity[]> {
|
||||
return this.personRepository
|
||||
.createQueryBuilder('person')
|
||||
.leftJoin('person.faces', 'face')
|
||||
.where('person.ownerId = :userId', { userId })
|
||||
.andWhere('LOWER(person.name) LIKE :name', { name: `${personName.toLowerCase()}%` })
|
||||
.limit(20)
|
||||
.getMany();
|
||||
}
|
||||
|
||||
getAssets(personId: string): Promise<AssetEntity[]> {
|
||||
return this.assetRepository.find({
|
||||
where: {
|
||||
|
||||
@@ -9,6 +9,8 @@ export const newPersonRepositoryMock = (): jest.Mocked<IPersonRepository> => {
|
||||
getAssets: jest.fn(),
|
||||
getAllWithoutFaces: jest.fn(),
|
||||
|
||||
getByName: jest.fn(),
|
||||
|
||||
create: jest.fn(),
|
||||
update: jest.fn(),
|
||||
deleteAll: jest.fn(),
|
||||
|
||||
Reference in New Issue
Block a user