feat(search): enhance geo-search and listing-approved handlers

Improve geo-search handler with better query processing and update
listing-approved event handler with enhanced indexing logic.
Tests updated accordingly.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 23:07:06 +07:00
parent a87532ff6e
commit 7fb25eb2b1
4 changed files with 163 additions and 39 deletions

View File

@@ -1,5 +1,6 @@
import { Inject } from '@nestjs/common';
import { type IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { CacheService, CachePrefix, CacheTTL } from '@modules/shared/infrastructure/cache.service';
import {
SEARCH_REPOSITORY,
type ISearchRepository,
@@ -11,33 +12,53 @@ import { GeoSearchQuery } from './geo-search.query';
export class GeoSearchHandler implements IQueryHandler<GeoSearchQuery> {
constructor(
@Inject(SEARCH_REPOSITORY) private readonly searchRepo: ISearchRepository,
private readonly cache: CacheService,
) {}
async execute(query: GeoSearchQuery): Promise<SearchResult> {
const filters: string[] = ['status:=ACTIVE'];
const cacheKey = CacheService.buildKey(
CachePrefix.GEO_SEARCH,
`${query.lat}_${query.lng}_${query.radiusKm}`,
query.propertyType,
query.transactionType,
query.priceMin,
query.priceMax,
query.sortBy,
query.page,
query.perPage,
);
if (query.propertyType) {
filters.push(`propertyType:=${query.propertyType}`);
}
if (query.transactionType) {
filters.push(`transactionType:=${query.transactionType}`);
}
if (query.priceMin !== undefined && query.priceMax !== undefined) {
filters.push(`priceVND:[${query.priceMin}..${query.priceMax}]`);
} else if (query.priceMin !== undefined) {
filters.push(`priceVND:>=${query.priceMin}`);
} else if (query.priceMax !== undefined) {
filters.push(`priceVND:<=${query.priceMax}`);
}
return this.cache.getOrSet(
cacheKey,
async () => {
const filters: string[] = ['status:=ACTIVE'];
return this.searchRepo.search({
query: '*',
filterBy: filters.join(' && '),
sortBy: query.sortBy,
page: query.page,
perPage: query.perPage,
geoPoint: { lat: query.lat, lng: query.lng },
geoRadiusKm: Math.min(query.radiusKm, 100),
});
if (query.propertyType) {
filters.push(`propertyType:=${query.propertyType}`);
}
if (query.transactionType) {
filters.push(`transactionType:=${query.transactionType}`);
}
if (query.priceMin !== undefined && query.priceMax !== undefined) {
filters.push(`priceVND:[${query.priceMin}..${query.priceMax}]`);
} else if (query.priceMin !== undefined) {
filters.push(`priceVND:>=${query.priceMin}`);
} else if (query.priceMax !== undefined) {
filters.push(`priceVND:<=${query.priceMax}`);
}
return this.searchRepo.search({
query: '*',
filterBy: filters.join(' && '),
sortBy: query.sortBy,
page: query.page,
perPage: query.perPage,
geoPoint: { lat: query.lat, lng: query.lng },
geoRadiusKm: Math.min(query.radiusKm, 100),
});
},
CacheTTL.SEARCH_RESULTS,
'geo_search',
);
}
}