feat(server): search unknown place (#10866)
* Allow submission of null country
* Update searchAssetBuilder to handle nulls
andWhere({country:null}) produces `"exifInfo"."country" = NULL`. We want
`"exifInfo"."country" IS NULL`, so we have to treat NULL as a special
case
* Allow null country in frontend
* Make the query code a bit more straightforward
* Remove unused brackets import
* Remove log message
* Don't change whitespace for no reason
* Fix prettier style issue
* Update search.dto.ts validators per @jrasm91's recommendation
* Update api types
* Combine null country and state into one guard clause
* chore: clean up
* chore: add e2e for null/empty city, state, country search
* refactor: server returns suggestion for null values
* chore: clean up
---------
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
Co-authored-by: Jason Rasmussen <jason@rasm.me>
This commit is contained in:
@@ -57,49 +57,42 @@ export class MetadataRepository implements IMetadataRepository {
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID] })
|
||||
async getCountries(userId: string): Promise<string[]> {
|
||||
const entity = await this.exifRepository
|
||||
const results = await this.exifRepository
|
||||
.createQueryBuilder('exif')
|
||||
.leftJoin('exif.asset', 'asset')
|
||||
.where('asset.ownerId = :userId', { userId })
|
||||
.andWhere('exif.country IS NOT NULL')
|
||||
.select('exif.country')
|
||||
.select('exif.country', 'country')
|
||||
.distinctOn(['exif.country'])
|
||||
.getMany();
|
||||
.getRawMany<{ country: string }>();
|
||||
|
||||
return entity.map((e) => e.country ?? '').filter((c) => c !== '');
|
||||
return results.map(({ country }) => country).filter((item) => item !== '');
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
||||
async getStates(userId: string, country: string | undefined): Promise<string[]> {
|
||||
let result: ExifEntity[] = [];
|
||||
|
||||
const query = this.exifRepository
|
||||
.createQueryBuilder('exif')
|
||||
.leftJoin('exif.asset', 'asset')
|
||||
.where('asset.ownerId = :userId', { userId })
|
||||
.andWhere('exif.state IS NOT NULL')
|
||||
.select('exif.state')
|
||||
.select('exif.state', 'state')
|
||||
.distinctOn(['exif.state']);
|
||||
|
||||
if (country) {
|
||||
query.andWhere('exif.country = :country', { country });
|
||||
}
|
||||
|
||||
result = await query.getMany();
|
||||
const result = await query.getRawMany<{ state: string }>();
|
||||
|
||||
return result.map((entity) => entity.state ?? '').filter((s) => s !== '');
|
||||
return result.map(({ state }) => state).filter((item) => item !== '');
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING, DummyValue.STRING] })
|
||||
async getCities(userId: string, country: string | undefined, state: string | undefined): Promise<string[]> {
|
||||
let result: ExifEntity[] = [];
|
||||
|
||||
const query = this.exifRepository
|
||||
.createQueryBuilder('exif')
|
||||
.leftJoin('exif.asset', 'asset')
|
||||
.where('asset.ownerId = :userId', { userId })
|
||||
.andWhere('exif.city IS NOT NULL')
|
||||
.select('exif.city')
|
||||
.select('exif.city', 'city')
|
||||
.distinctOn(['exif.city']);
|
||||
|
||||
if (country) {
|
||||
@@ -110,50 +103,42 @@ export class MetadataRepository implements IMetadataRepository {
|
||||
query.andWhere('exif.state = :state', { state });
|
||||
}
|
||||
|
||||
result = await query.getMany();
|
||||
const results = await query.getRawMany<{ city: string }>();
|
||||
|
||||
return result.map((entity) => entity.city ?? '').filter((c) => c !== '');
|
||||
return results.map(({ city }) => city).filter((item) => item !== '');
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
||||
async getCameraMakes(userId: string, model: string | undefined): Promise<string[]> {
|
||||
let result: ExifEntity[] = [];
|
||||
|
||||
const query = this.exifRepository
|
||||
.createQueryBuilder('exif')
|
||||
.leftJoin('exif.asset', 'asset')
|
||||
.where('asset.ownerId = :userId', { userId })
|
||||
.andWhere('exif.make IS NOT NULL')
|
||||
.select('exif.make')
|
||||
.select('exif.make', 'make')
|
||||
.distinctOn(['exif.make']);
|
||||
|
||||
if (model) {
|
||||
query.andWhere('exif.model = :model', { model });
|
||||
}
|
||||
|
||||
result = await query.getMany();
|
||||
|
||||
return result.map((entity) => entity.make ?? '').filter((m) => m !== '');
|
||||
const results = await query.getRawMany<{ make: string }>();
|
||||
return results.map(({ make }) => make).filter((item) => item !== '');
|
||||
}
|
||||
|
||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.STRING] })
|
||||
async getCameraModels(userId: string, make: string | undefined): Promise<string[]> {
|
||||
let result: ExifEntity[] = [];
|
||||
|
||||
const query = this.exifRepository
|
||||
.createQueryBuilder('exif')
|
||||
.leftJoin('exif.asset', 'asset')
|
||||
.where('asset.ownerId = :userId', { userId })
|
||||
.andWhere('exif.model IS NOT NULL')
|
||||
.select('exif.model')
|
||||
.select('exif.model', 'model')
|
||||
.distinctOn(['exif.model']);
|
||||
|
||||
if (make) {
|
||||
query.andWhere('exif.make = :make', { make });
|
||||
}
|
||||
|
||||
result = await query.getMany();
|
||||
|
||||
return result.map((entity) => entity.model ?? '').filter((m) => m !== '');
|
||||
const results = await query.getRawMany<{ model: string }>();
|
||||
return results.map(({ model }) => model).filter((item) => item !== '');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user