fix(server): search suggestions include partner assets (#12269)

search suggestions now include partner assets

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
Lukas 2024-09-05 10:12:46 -04:00 committed by GitHub
parent 259bc8a6b0
commit 27e283e724
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 26 additions and 25 deletions

View File

@ -53,9 +53,9 @@ export interface IMetadataRepository {
readTags(path: string): Promise<ImmichTags | null>; readTags(path: string): Promise<ImmichTags | null>;
writeTags(path: string, tags: Partial<Tags>): Promise<void>; writeTags(path: string, tags: Partial<Tags>): Promise<void>;
extractBinaryTag(tagName: string, path: string): Promise<Buffer>; extractBinaryTag(tagName: string, path: string): Promise<Buffer>;
getCountries(userId: string): Promise<Array<string | null>>; getCountries(userIds: string[]): Promise<Array<string | null>>;
getStates(userId: string, country?: string): Promise<Array<string | null>>; getStates(userIds: string[], country?: string): Promise<Array<string | null>>;
getCities(userId: string, country?: string, state?: string): Promise<Array<string | null>>; getCities(userIds: string[], country?: string, state?: string): Promise<Array<string | null>>;
getCameraMakes(userId: string, model?: string): Promise<Array<string | null>>; getCameraMakes(userIds: string[], model?: string): Promise<Array<string | null>>;
getCameraModels(userId: string, make?: string): Promise<Array<string | null>>; getCameraModels(userIds: string[], make?: string): Promise<Array<string | null>>;
} }

View File

@ -56,11 +56,11 @@ export class MetadataRepository implements IMetadataRepository {
} }
@GenerateSql({ params: [DummyValue.UUID] }) @GenerateSql({ params: [DummyValue.UUID] })
async getCountries(userId: string): Promise<string[]> { async getCountries(userIds: string[]): Promise<string[]> {
const results = await this.exifRepository const results = await this.exifRepository
.createQueryBuilder('exif') .createQueryBuilder('exif')
.leftJoin('exif.asset', 'asset') .leftJoin('exif.asset', 'asset')
.where('asset.ownerId = :userId', { userId }) .where('asset.ownerId IN (:...userIds )', { userIds })
.select('exif.country', 'country') .select('exif.country', 'country')
.distinctOn(['exif.country']) .distinctOn(['exif.country'])
.getRawMany<{ country: string }>(); .getRawMany<{ country: string }>();
@ -69,11 +69,11 @@ export class MetadataRepository implements IMetadataRepository {
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
async getStates(userId: string, country: string | undefined): Promise<string[]> { async getStates(userIds: string[], country: string | undefined): Promise<string[]> {
const query = this.exifRepository const query = this.exifRepository
.createQueryBuilder('exif') .createQueryBuilder('exif')
.leftJoin('exif.asset', 'asset') .leftJoin('exif.asset', 'asset')
.where('asset.ownerId = :userId', { userId }) .where('asset.ownerId IN (:...userIds )', { userIds })
.select('exif.state', 'state') .select('exif.state', 'state')
.distinctOn(['exif.state']); .distinctOn(['exif.state']);
@ -87,11 +87,11 @@ export class MetadataRepository implements IMetadataRepository {
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING, DummyValue.STRING] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING, DummyValue.STRING] })
async getCities(userId: string, country: string | undefined, state: string | undefined): Promise<string[]> { async getCities(userIds: string[], country: string | undefined, state: string | undefined): Promise<string[]> {
const query = this.exifRepository const query = this.exifRepository
.createQueryBuilder('exif') .createQueryBuilder('exif')
.leftJoin('exif.asset', 'asset') .leftJoin('exif.asset', 'asset')
.where('asset.ownerId = :userId', { userId }) .where('asset.ownerId IN (:...userIds )', { userIds })
.select('exif.city', 'city') .select('exif.city', 'city')
.distinctOn(['exif.city']); .distinctOn(['exif.city']);
@ -109,11 +109,11 @@ export class MetadataRepository implements IMetadataRepository {
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
async getCameraMakes(userId: string, model: string | undefined): Promise<string[]> { async getCameraMakes(userIds: string[], model: string | undefined): Promise<string[]> {
const query = this.exifRepository const query = this.exifRepository
.createQueryBuilder('exif') .createQueryBuilder('exif')
.leftJoin('exif.asset', 'asset') .leftJoin('exif.asset', 'asset')
.where('asset.ownerId = :userId', { userId }) .where('asset.ownerId IN (:...userIds )', { userIds })
.select('exif.make', 'make') .select('exif.make', 'make')
.distinctOn(['exif.make']); .distinctOn(['exif.make']);
@ -126,11 +126,11 @@ export class MetadataRepository implements IMetadataRepository {
} }
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] }) @GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
async getCameraModels(userId: string, make: string | undefined): Promise<string[]> { async getCameraModels(userIds: string[], make: string | undefined): Promise<string[]> {
const query = this.exifRepository const query = this.exifRepository
.createQueryBuilder('exif') .createQueryBuilder('exif')
.leftJoin('exif.asset', 'asset') .leftJoin('exif.asset', 'asset')
.where('asset.ownerId = :userId', { userId }) .where('asset.ownerId IN (:...userIds )', { userIds })
.select('exif.model', 'model') .select('exif.model', 'model')
.distinctOn(['exif.model']); .distinctOn(['exif.model']);

View File

@ -103,7 +103,7 @@ describe(SearchService.name, () => {
await expect( await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.COUNTRY }), sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.COUNTRY }),
).resolves.toEqual(['USA', null]); ).resolves.toEqual(['USA', null]);
expect(metadataMock.getCountries).toHaveBeenCalledWith(authStub.user1.user.id); expect(metadataMock.getCountries).toHaveBeenCalledWith([authStub.user1.user.id]);
}); });
it('should return search suggestions (without null)', async () => { it('should return search suggestions (without null)', async () => {
@ -111,7 +111,7 @@ describe(SearchService.name, () => {
await expect( await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.COUNTRY }), sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.COUNTRY }),
).resolves.toEqual(['USA']); ).resolves.toEqual(['USA']);
expect(metadataMock.getCountries).toHaveBeenCalledWith(authStub.user1.user.id); expect(metadataMock.getCountries).toHaveBeenCalledWith([authStub.user1.user.id]);
}); });
}); });
}); });

View File

@ -121,26 +121,27 @@ export class SearchService {
} }
async getSearchSuggestions(auth: AuthDto, dto: SearchSuggestionRequestDto) { async getSearchSuggestions(auth: AuthDto, dto: SearchSuggestionRequestDto) {
const results = await this.getSuggestions(auth.user.id, dto); const userIds = await this.getUserIdsToSearch(auth);
const results = await this.getSuggestions(userIds, dto);
return results.filter((result) => (dto.includeNull ? true : result !== null)); return results.filter((result) => (dto.includeNull ? true : result !== null));
} }
private getSuggestions(userId: string, dto: SearchSuggestionRequestDto) { private getSuggestions(userIds: string[], dto: SearchSuggestionRequestDto) {
switch (dto.type) { switch (dto.type) {
case SearchSuggestionType.COUNTRY: { case SearchSuggestionType.COUNTRY: {
return this.metadataRepository.getCountries(userId); return this.metadataRepository.getCountries(userIds);
} }
case SearchSuggestionType.STATE: { case SearchSuggestionType.STATE: {
return this.metadataRepository.getStates(userId, dto.country); return this.metadataRepository.getStates(userIds, dto.country);
} }
case SearchSuggestionType.CITY: { case SearchSuggestionType.CITY: {
return this.metadataRepository.getCities(userId, dto.country, dto.state); return this.metadataRepository.getCities(userIds, dto.country, dto.state);
} }
case SearchSuggestionType.CAMERA_MAKE: { case SearchSuggestionType.CAMERA_MAKE: {
return this.metadataRepository.getCameraMakes(userId, dto.model); return this.metadataRepository.getCameraMakes(userIds, dto.model);
} }
case SearchSuggestionType.CAMERA_MODEL: { case SearchSuggestionType.CAMERA_MODEL: {
return this.metadataRepository.getCameraModels(userId, dto.make); return this.metadataRepository.getCameraModels(userIds, dto.make);
} }
default: { default: {
return []; return [];

View File

@ -5,7 +5,7 @@ export const newPartnerRepositoryMock = (): Mocked<IPartnerRepository> => {
return { return {
create: vitest.fn(), create: vitest.fn(),
remove: vitest.fn(), remove: vitest.fn(),
getAll: vitest.fn(), getAll: vitest.fn().mockResolvedValue([]),
get: vitest.fn(), get: vitest.fn(),
update: vitest.fn(), update: vitest.fn(),
}; };