-
Notifications
You must be signed in to change notification settings - Fork 1
[feat] 가게가 웨이블존인지 여부를 판단하는 기능 구현 #136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -123,4 +123,99 @@ public Slice<WaybleZoneSearchResponseDto> searchWaybleZonesByCondition(WaybleZon | |
|
|
||
| return new SliceImpl<>(dtos, pageable, hasNext); | ||
| } | ||
|
|
||
| /** | ||
| * 30m 이내이고 이름이 유사한 WaybleZone 찾기 | ||
| * @param cond 검색 조건 (위도, 경도, 이름 포함) | ||
| * @return 조건에 맞는 첫 번째 결과 또는 null | ||
| */ | ||
| public WaybleZoneSearchResponseDto findSimilarWaybleZone(WaybleZoneSearchConditionDto cond) { | ||
| if (cond.zoneName() == null || cond.zoneName().isBlank()) { | ||
| return null; | ||
| } | ||
|
|
||
| // 30m 이내 검색 | ||
| Query query = Query.of(q -> q | ||
| .bool(b -> { | ||
| // 이름 유사도 검색 (fuzzy + match 조합) | ||
| b.should(s -> s | ||
| .match(m -> m | ||
| .field("zoneName") | ||
| .query(cond.zoneName()) | ||
| .boost(2.0f) // 정확한 매치에 높은 점수 | ||
| ) | ||
| ); | ||
| b.should(s -> s | ||
| .fuzzy(f -> f | ||
| .field("zoneName") | ||
| .value(cond.zoneName()) | ||
| .fuzziness("AUTO") // 오타 허용 | ||
| .boost(1.5f) | ||
| ) | ||
| ); | ||
| // 부분 매치도 포함 (공백 제거 후 검색) | ||
| String cleanedName = cond.zoneName().replaceAll("\\s+", ""); | ||
| b.should(s -> s | ||
| .wildcard(w -> w | ||
| .field("zoneName") | ||
| .value("*" + cleanedName + "*") | ||
| .boost(1.0f) | ||
| ) | ||
| ); | ||
|
|
||
| // 최소 하나의 should 조건은 만족해야 함 | ||
| b.minimumShouldMatch("1"); | ||
|
|
||
| // 30m 이내 필터 | ||
| b.filter(f -> f | ||
| .geoDistance(gd -> gd | ||
| .field("address.location") | ||
| .location(loc -> loc | ||
| .latlon(ll -> ll | ||
| .lat(cond.latitude()) | ||
| .lon(cond.longitude()) | ||
| ) | ||
| ) | ||
| .distance("30m") | ||
| ) | ||
| ); | ||
| return b; | ||
| }) | ||
| ); | ||
|
|
||
| // 정렬: 점수 + 거리 조합 | ||
| SortOptions scoreSort = SortOptions.of(s -> s.score(sc -> sc.order(SortOrder.Desc))); | ||
| SortOptions geoSort = SortOptions.of(s -> s | ||
| .geoDistance(gds -> gds | ||
| .field("address.location") | ||
| .location(GeoLocation.of(gl -> gl | ||
| .latlon(ll -> ll | ||
| .lat(cond.latitude()) | ||
| .lon(cond.longitude()) | ||
| ) | ||
| )) | ||
| .order(SortOrder.Asc) | ||
| ) | ||
| ); | ||
|
Comment on lines
+186
to
+199
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain거리 sort 값 인덱스 의존 취약성 — 정렬값 배열 구조 변경 시 오동작 위험 현재 코드는 두 번째 정렬값을 거리로 가정합니다(Line 216). 수정 제안: - SortOptions scoreSort = SortOptions.of(s -> s.score(sc -> sc.order(SortOrder.Desc)));
+ SortOptions scoreSort = SortOptions.of(s -> s.score(sc -> sc.order(SortOrder.Desc)));
SortOptions geoSort = SortOptions.of(s -> s
.geoDistance(gds -> gds
.field("address.location")
.location(GeoLocation.of(gl -> gl
.latlon(ll -> ll
.lat(cond.latitude())
.lon(cond.longitude())
)
))
.order(SortOrder.Asc)
)
);
...
- WaybleZoneDocument doc = hits.getSearchHit(0).getContent();
- Double distanceInMeters = (Double) hits.getSearchHit(0).getSortValues().get(1); // 거리는 두 번째 정렬값
+ WaybleZoneDocument doc = hits.getSearchHit(0).getContent();
+ java.util.List<Object> sortValues = hits.getSearchHit(0).getSortValues();
+ Double distanceInMeters = (Double) sortValues.get(sortValues.size() - 1); // 마지막 정렬값을 거리로 취급
Double distanceInKm = distanceInMeters / 1000.0;검증 권장: 정렬값 배열에 Also applies to: 215-219 🏁 Script executed: #!/bin/bash
# 1. WaybleZoneQuerySearchRepository.java 내 getSearchHit 사용 위치 확인
grep -R "hits.getSearchHit" -n src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java -A3
# 2. getSortValues 호출 위치 및 컨텍스트 확인
grep -R "getSortValues" -n src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java -A3
# 3. SearchRequest 에 전달되는 SortOptions 리스트 생성부 확인
grep -R "searchAsync" -n src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java -A5Length of output: 1229 거리 정렬값 인덱스 참조 방어 로직 필요 수정 대상 파일:
제안된 수정: - Double distanceInMeters = (Double) hit.getSortValues().get(0);
+ java.util.List<Object> sortValues = hit.getSortValues();
+ Double distanceInMeters = (Double) sortValues.get(sortValues.size() - 1);
- Double distanceInMeters = (Double) hits.getSearchHit(0).getSortValues().get(1); // 거리는 두 번째 정렬값
+ java.util.List<Object> sortValues = hits.getSearchHit(0).getSortValues();
+ Double distanceInMeters = (Double) sortValues.get(sortValues.size() - 1); // 마지막 정렬값을 거리로 취급🤖 Prompt for AI Agents |
||
|
|
||
| NativeQuery nativeQuery = NativeQuery.builder() | ||
| .withQuery(query) | ||
| .withSort(scoreSort) | ||
| .withSort(geoSort) | ||
| .withPageable(PageRequest.of(0, 1)) // 첫 번째 결과만 | ||
| .build(); | ||
|
|
||
| SearchHits<WaybleZoneDocument> hits = | ||
| operations.search(nativeQuery, WaybleZoneDocument.class, INDEX); | ||
|
|
||
| if (hits.isEmpty()) { | ||
| return null; | ||
| } | ||
|
|
||
| WaybleZoneDocument doc = hits.getSearchHit(0).getContent(); | ||
| Double distanceInMeters = (Double) hits.getSearchHit(0).getSortValues().get(1); // 거리는 두 번째 정렬값 | ||
| Double distanceInKm = distanceInMeters / 1000.0; | ||
|
|
||
| return WaybleZoneSearchResponseDto.from(doc, distanceInKm); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
공백 허용 유사 매치(wildcard) 로직이 의도대로 동작하지 않을 가능성 높음
입력의 공백을 제거한 cleanedName으로
zoneName필드에*cleanedName*와일드카드 매칭을 수행하면, 인덱스의 실제 값에 공백이 포함된 경우(예: "스 타 벅 스") 매칭이 실패할 수 있습니다. 공백을 “임의 문자”로 간주하도록 입력의 공백을*로 치환하는 편이 의도(공백 무시)에 더 부합합니다. 또한 wildcard는 일반적으로 keyword 서브필드에서 수행하는 것이 안전합니다.아래와 같이 수정 제안합니다:
참고: 매핑에 zoneName.keyword가 없다면, 매핑/인덱싱 단계에서 공백 제거나 소문자 정규화를 수행하는 별도의 서브필드를 추가하는 것이 가장 견고합니다.
📝 Committable suggestion
🤖 Prompt for AI Agents