1+ import { Injectable } from '@nestjs/common' ;
2+ import { PrismaService } from '../../database/prisma/prisma.service' ;
3+ import { PropertySearchDto } from '../dto/property-search.dto' ;
4+ import { SearchAnalyticsService } from './search-analytics.service' ;
5+
6+ @Injectable ( )
7+ export class PropertySearchService {
8+ constructor (
9+ private readonly prisma : PrismaService ,
10+ private readonly analytics : SearchAnalyticsService ,
11+ ) { }
12+ async search ( dto : PropertySearchDto , userId ?: string ) {
13+ const {
14+ latitude,
15+ longitude,
16+ radiusKm = 5 ,
17+ page = 1 ,
18+ limit = 10 ,
19+ minPrice,
20+ maxPrice,
21+ location,
22+ status = 'PUBLISHED' ,
23+ } = dto ;
24+
25+ const offset = ( page - 1 ) * limit ;
26+
27+ // Geospatial search
28+ if ( latitude && longitude ) {
29+ return this . prisma . $queryRawUnsafe ( `
30+ SELECT *,
31+ ST_Distance(
32+ coordinates,
33+ ST_SetSRID(ST_MakePoint(${ longitude } , ${ latitude } ), 4326)
34+ ) AS distance
35+ FROM properties
36+ WHERE status = '${ status } '
37+ ${ minPrice ? `AND price >= ${ minPrice } ` : '' }
38+ ${ maxPrice ? `AND price <= ${ maxPrice } ` : '' }
39+ ${ location ? `AND location ILIKE '%${ location } %'` : '' }
40+ AND ST_DWithin(
41+ coordinates,
42+ ST_SetSRID(ST_MakePoint(${ longitude } , ${ latitude } ), 4326),
43+ ${ radiusKm * 1000 }
44+ )
45+ ORDER BY distance ASC
46+ LIMIT ${ limit }
47+ OFFSET ${ offset } ;
48+ ` ) ;
49+ }
50+
51+ // Normal search
52+ return this . prisma . property . findMany ( {
53+ where : {
54+ status,
55+ ...( location && { location : { contains : location , mode : 'insensitive' } } ) ,
56+ ...( minPrice && { price : { gte : minPrice } } ) ,
57+ ...( maxPrice && { price : { lte : maxPrice } } ) ,
58+ } ,
59+ skip : offset ,
60+ take : limit ,
61+ orderBy : { createdAt : 'desc' } ,
62+ } ) ;
63+ }
64+
65+ private async geoSearch ( dto : PropertySearchDto ) {
66+ const {
67+ latitude,
68+ longitude,
69+ radiusKm = 5 ,
70+ page = 1 ,
71+ limit = 10 ,
72+ minPrice,
73+ maxPrice,
74+ location,
75+ status = 'PUBLISHED' ,
76+ } = dto ;
77+
78+ const offset = ( page - 1 ) * limit ;
79+
80+ return this . prisma . $queryRawUnsafe ( `
81+ SELECT *,
82+ ST_Distance(
83+ coordinates,
84+ ST_SetSRID(ST_MakePoint(${ longitude } , ${ latitude } ), 4326)
85+ ) AS distance
86+ FROM properties
87+ WHERE status = '${ status } '
88+ ${ minPrice ? `AND price >= ${ minPrice } ` : '' }
89+ ${ maxPrice ? `AND price <= ${ maxPrice } ` : '' }
90+ ${ location ? `AND location ILIKE '%${ location } %'` : '' }
91+ AND ST_DWithin(
92+ coordinates,
93+ ST_SetSRID(ST_MakePoint(${ longitude } , ${ latitude } ), 4326),
94+ ${ radiusKm * 1000 }
95+ )
96+ ORDER BY distance ASC
97+ LIMIT ${ limit }
98+ OFFSET ${ offset } ;
99+ ` ) ;
100+ }
101+
102+ private async normalSearch ( dto : PropertySearchDto ) {
103+ const {
104+ page = 1 ,
105+ limit = 10 ,
106+ minPrice,
107+ maxPrice,
108+ location,
109+ status = 'PUBLISHED' ,
110+ } = dto ;
111+
112+ const offset = ( page - 1 ) * limit ;
113+
114+ return this . prisma . property . findMany ( {
115+ where : {
116+ status,
117+ ...( location && { location : { contains : location , mode : 'insensitive' } } ) ,
118+ ...( minPrice && { price : { gte : minPrice } } ) ,
119+ ...( maxPrice && { price : { lte : maxPrice } } ) ,
120+ } ,
121+ skip : offset ,
122+ take : limit ,
123+ orderBy : { createdAt : 'desc' } ,
124+ } ) ;
125+ }
126+ }
0 commit comments