11package com .ll .commars .domain .restaurant .restaurantDoc .service ;
22
3+ import co .elastic .clients .elasticsearch .ElasticsearchClient ;
4+ import co .elastic .clients .elasticsearch ._types .GeoLocation ;
5+ import co .elastic .clients .elasticsearch ._types .LatLonGeoLocation ;
6+ import co .elastic .clients .elasticsearch ._types .query_dsl .*;
7+ import co .elastic .clients .elasticsearch .core .SearchRequest ;
8+ import co .elastic .clients .elasticsearch .core .SearchResponse ;
39import com .ll .commars .domain .restaurant .restaurantDoc .document .RestaurantDoc ;
410import com .ll .commars .domain .restaurant .restaurantDoc .repository .RestaurantDocRepository ;
511import lombok .RequiredArgsConstructor ;
612import org .springframework .stereotype .Service ;
713
14+ import java .io .IOException ;
815import java .util .List ;
16+ import java .util .stream .Collectors ;
917
1018@ Service
1119@ RequiredArgsConstructor
1220public class RestaurantDocService {
1321 private final RestaurantDocRepository restaurantDocRepository ;
22+ private final ElasticsearchClient elasticsearchClient ;
1423
1524 public RestaurantDoc write (String name , String details , Double averageRate , Double lat , Double lng ) {
1625 RestaurantDoc restaurantDoc = RestaurantDoc .builder ()
@@ -27,11 +36,60 @@ public void truncate() {
2736 restaurantDocRepository .deleteAll ();
2837 }
2938
30- public List <RestaurantDoc > searchByKeyword (String keyword ) {
31- return restaurantDocRepository .searchByKeyword (keyword );
32- }
39+ public List <RestaurantDoc > searchByKeyword (String keyword , double userLat , double userLng , String distance ) throws IOException {
40+ Query matchNameQuery = MatchQuery .of (m -> m
41+ .field ("name" )
42+ .query (keyword )
43+ .fuzziness ("AUTO" )
44+ )._toQuery ();
45+
46+ Query matchDetailsQuery = MatchQuery .of (m -> m
47+ .field ("details" )
48+ .query (keyword )
49+ .fuzziness ("AUTO" )
50+ )._toQuery ();
51+
52+ // ✅ 사용자 위치 기준 반경 검색 (Geo Distance)
53+ Query geoDistanceQuery = GeoDistanceQuery .of (g -> g
54+ .field ("location" ) // ES의 GeoPoint 필드
55+ .distance (distance ) // 검색 반경 (예: "50km")
56+ .location (GeoLocation .of (l -> l .latlon (LatLonGeoLocation .of (ll -> ll .lat (userLat ).lon (userLng ))))) // ✅ 수정된 부분
57+ )._toQuery ();
58+
59+ // 키워드 + 거리 필터 조합
60+ Query boolQuery = BoolQuery .of (b -> b
61+ .should (matchNameQuery )
62+ .should (matchDetailsQuery )
63+ .filter (geoDistanceQuery ) // ✅ 거리 필터 추가
64+ )._toQuery ();
65+
66+ // Function Score Query (평점 높은 곳 우선)
67+ FunctionScoreQuery functionScoreQuery = FunctionScoreQuery .of (f -> f
68+ .query (boolQuery )
69+ .functions (FunctionScore .of (fs -> fs
70+ .fieldValueFactor (FieldValueFactorScoreFunction .of (fv -> fv
71+ .field ("average_rate" )
72+ .factor (1.5 )
73+ .modifier (FieldValueFactorModifier .Sqrt )
74+ ))
75+ ))
76+ );
77+
78+ // 검색 요청
79+ SearchRequest searchRequest = SearchRequest .of (s -> s
80+ .index ("es_restaurants" )
81+ .query (functionScoreQuery ._toQuery ())
82+ );
83+
84+ // 검색 실행
85+ SearchResponse <RestaurantDoc > response = elasticsearchClient .search (searchRequest , RestaurantDoc .class );
86+
87+ return response .hits ().hits ().stream ()
88+ .map (hit -> hit .source ())
89+ .collect (Collectors .toList ());
90+ }
3391
34- public List <RestaurantDoc > showSortByRate () {
92+ public List <RestaurantDoc > showSortByRate () {
3593 return restaurantDocRepository .findAllByOrderByAverageRateDesc ();
3694 }
3795
0 commit comments