refactor(server): use includeNull in query for search suggestions (#14626)

* use `includeNull`

* push down `includeNull` into query, inner joins

* remove filter

* update sql

* fix tests

* maybe fix e2e

* more e2e tests

* handle no exif row

* whoops

* update sql
This commit is contained in:
Mert
2024-12-10 16:22:37 -05:00
committed by GitHub
parent 60c783bbe9
commit 25ca3b1124
6 changed files with 259 additions and 52 deletions

View File

@@ -59,20 +59,84 @@ describe(SearchService.name, () => {
});
describe('getSearchSuggestions', () => {
it('should return search suggestions (including null)', async () => {
searchMock.getCountries.mockResolvedValue(['USA', null]);
it('should return search suggestions for country', async () => {
searchMock.getCountries.mockResolvedValue(['USA']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.COUNTRY }),
).resolves.toEqual(['USA']);
expect(searchMock.getCountries).toHaveBeenCalledWith([authStub.user1.user.id]);
});
it('should return search suggestions for country (including null)', async () => {
searchMock.getCountries.mockResolvedValue(['USA']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.COUNTRY }),
).resolves.toEqual(['USA', null]);
expect(searchMock.getCountries).toHaveBeenCalledWith([authStub.user1.user.id]);
});
it('should return search suggestions (without null)', async () => {
searchMock.getCountries.mockResolvedValue(['USA', null]);
it('should return search suggestions for state', async () => {
searchMock.getStates.mockResolvedValue(['California']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.COUNTRY }),
).resolves.toEqual(['USA']);
expect(searchMock.getCountries).toHaveBeenCalledWith([authStub.user1.user.id]);
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.STATE }),
).resolves.toEqual(['California']);
expect(searchMock.getStates).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
});
it('should return search suggestions for state (including null)', async () => {
searchMock.getStates.mockResolvedValue(['California']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.STATE }),
).resolves.toEqual(['California', null]);
expect(searchMock.getStates).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
});
it('should return search suggestions for city', async () => {
searchMock.getCities.mockResolvedValue(['Denver']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.CITY }),
).resolves.toEqual(['Denver']);
expect(searchMock.getCities).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
});
it('should return search suggestions for city (including null)', async () => {
searchMock.getCities.mockResolvedValue(['Denver']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.CITY }),
).resolves.toEqual(['Denver', null]);
expect(searchMock.getCities).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
});
it('should return search suggestions for camera make', async () => {
searchMock.getCameraMakes.mockResolvedValue(['Nikon']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.CAMERA_MAKE }),
).resolves.toEqual(['Nikon']);
expect(searchMock.getCameraMakes).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
});
it('should return search suggestions for camera make (including null)', async () => {
searchMock.getCameraMakes.mockResolvedValue(['Nikon']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.CAMERA_MAKE }),
).resolves.toEqual(['Nikon', null]);
expect(searchMock.getCameraMakes).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
});
it('should return search suggestions for camera model', async () => {
searchMock.getCameraModels.mockResolvedValue(['Fujifilm X100VI']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: false, type: SearchSuggestionType.CAMERA_MODEL }),
).resolves.toEqual(['Fujifilm X100VI']);
expect(searchMock.getCameraModels).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
});
it('should return search suggestions for camera model (including null)', async () => {
searchMock.getCameraModels.mockResolvedValue(['Fujifilm X100VI']);
await expect(
sut.getSearchSuggestions(authStub.user1, { includeNull: true, type: SearchSuggestionType.CAMERA_MODEL }),
).resolves.toEqual(['Fujifilm X100VI', null]);
expect(searchMock.getCameraModels).toHaveBeenCalledWith([authStub.user1.user.id], expect.anything());
});
});
});

View File

@@ -108,8 +108,11 @@ export class SearchService extends BaseService {
async getSearchSuggestions(auth: AuthDto, dto: SearchSuggestionRequestDto) {
const userIds = await this.getUserIdsToSearch(auth);
const results = await this.getSuggestions(userIds, dto);
return results.filter((result) => (dto.includeNull ? true : result !== null));
const suggestions = await this.getSuggestions(userIds, dto);
if (dto.includeNull) {
suggestions.push(null);
}
return suggestions;
}
private getSuggestions(userIds: string[], dto: SearchSuggestionRequestDto) {
@@ -118,19 +121,19 @@ export class SearchService extends BaseService {
return this.searchRepository.getCountries(userIds);
}
case SearchSuggestionType.STATE: {
return this.searchRepository.getStates(userIds, dto.country);
return this.searchRepository.getStates(userIds, dto);
}
case SearchSuggestionType.CITY: {
return this.searchRepository.getCities(userIds, dto.country, dto.state);
return this.searchRepository.getCities(userIds, dto);
}
case SearchSuggestionType.CAMERA_MAKE: {
return this.searchRepository.getCameraMakes(userIds, dto.model);
return this.searchRepository.getCameraMakes(userIds, dto);
}
case SearchSuggestionType.CAMERA_MODEL: {
return this.searchRepository.getCameraModels(userIds, dto.make);
return this.searchRepository.getCameraModels(userIds, dto);
}
default: {
return [];
return [] as (string | null)[];
}
}
}