-
Notifications
You must be signed in to change notification settings - Fork 1
[feat] 웨이블존 일치 여부 검증 알고리즘 성능 개선 #164
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
Conversation
WalkthroughElasticsearch 기반의 가중치/퍼지/와일드카드 복합 쿼리를 제거하고, 30m 내 후보 조회(ES geoDistance) 후 메모리 내 텍스트 유사도 검사(정규화, 포함, Levenshtein, Jaccard)로 첫 매칭을 반환하도록 변경. 거리 계산/매핑 제거. 통합 테스트는 샘플 수 확대(100→1000)와 성능 로그 추가, 거리 검증 삭제. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Service/Repo as WaybleZoneQuerySearchRepository
participant ES as Elasticsearch
Client->>Service/Repo: findSimilarWaybleZone(name, lat, lon)
Service/Repo->>ES: geoDistance query (within 30m, size=10)
ES-->>Service/Repo: Candidate documents
loop In-memory similarity checks
Service/Repo->>Service/Repo: normalize + contains/Levenshtein/Jaccard
end
Service/Repo-->>Client: First matching zone (distance=null) or null
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (1)
593-599: distance가 null일 때 String.format("%.3f", null)로 인한 NPE 가능성/validate 경로는 distance를 null로 반환하도록 변경되었는데, 현재 로그 라인이 double 포맷을 강제해 NPE가 발생할 수 있습니다. null-safe 출력으로 수정하세요.
- System.out.println(" 거리: " + String.format("%.3f km", dto.distance())); + Double distance = dto.distance(); + System.out.println(" 거리: " + (distance == null ? "N/A" : String.format("%.3f km", distance)));
🧹 Nitpick comments (6)
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (1)
83-83: 샘플 수 1000개는 CI에서 부담 가능성 → 시스템 프로퍼티로 조정 가능하게성능 실험 의도는 이해하지만, CI/로컬 환경에 따라 시간/메모리 비용이 커질 수 있습니다. 기본값은 유지하되, 환경별로 조절 가능하도록 하는 편이 안전합니다.
아래처럼 시스템 프로퍼티(예: IT_SAMPLES)로 오버라이드 가능하게 하세요.
- private static final int SAMPLES = 1000; + private static final int SAMPLES = Integer.parseInt(System.getProperty("IT_SAMPLES", "1000"));src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (5)
129-147: 첫 매칭 반환은 비결정적일 수 있음 → 후보를 거리 오름차순으로 정렬 권장현재 findNearbyZones가 정렬 없이 반환되므로 ES 내부 순서에 따라 첫 매칭이 달라질 수 있습니다. 근접 후보에서 텍스트 매칭을 하되, 거리 오름차순으로 정렬 후 첫 매칭을 선택하면 결정성이 올라가고 UX도 개선됩니다.
아래 findNearbyZones 정렬 추가 제안을 함께 적용하세요.
153-168: 후보 조회: 거리 정렬 추가 + 후보 상한 여유 확보
- 정렬: 거리 오름차순 정렬을 추가해 결정성/일관성 향상.
- 상한: 30m 내 ‘보통 10개 미만’ 가정은 밀집 지역에서 깨질 수 있습니다. 30m에서도 10개 이상이 가능하므로 상한을 조금 늘려 안전하게 가져오길 권장합니다(예: 50).
private List<WaybleZoneDocument> findNearbyZones(WaybleZoneSearchConditionDto cond) { Query geoQuery = Query.of(q -> q .geoDistance(gd -> gd .field("address.location") .location(loc -> loc.latlon(ll -> ll .lat(cond.latitude()) .lon(cond.longitude()) )) .distance("30m") ) ); - NativeQuery nativeQuery = NativeQuery.builder() - .withQuery(geoQuery) - .withPageable(PageRequest.of(0, 10)) // 30m 이내는 보통 10개 미만 - .build(); + 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) + ) + ); + + NativeQuery nativeQuery = NativeQuery.builder() + .withQuery(geoQuery) + .withSort(geoSort) + .withPageable(PageRequest.of(0, 50)) // 밀집 지역 고려 여유분 확보 + .build();가능하면 매직 넘버는 상수로 올리길 권장합니다.
추가(클래스 상단 예시):
private static final String VALIDATION_RADIUS = "30m"; private static final int VALIDATION_CANDIDATE_LIMIT = 50;그리고 distance("30m"), PageRequest.of(0, 50) 부분을 상수로 교체하세요.
181-206: 짧은 질의(1글자 등)에서 과매칭 가능성 → contains 조건 최소 길이 가드 권장normalize 후 1~2글자 질의는 거의 모든 후보에 매칭될 수 있습니다. contains 기반 판정을 최소 길이 가드로 약화시키면 오탐을 줄일 수 있습니다.
- // 2. 포함 관계 (기존 wildcard와 유사) - if (normalizedZone.contains(normalizedSearch) || - normalizedSearch.contains(normalizedZone)) { + // 2. 포함 관계 (기존 wildcard와 유사) — 너무 짧은 토큰은 과매칭 방지 + boolean zoneContainsSearch = normalizedSearch.length() >= 2 && normalizedZone.contains(normalizedSearch); + boolean searchContainsZone = normalizedZone.length() >= 2 && normalizedSearch.contains(normalizedZone); + if (zoneContainsSearch || searchContainsZone) { return true; }
211-215: 정규화에 유니코드 정규화(NFKC) 추가 제안특수문자/전각/반각/조합형 등의 표현 차이를 줄이려면 유니코드 정규화(NFKC)를 먼저 수행하는 것이 안전합니다.
- private String normalize(String text) { - return text.replaceAll("\\s+", "") // 공백 제거 - .replaceAll("[^가-힣a-zA-Z0-9]", "") // 특수문자 제거 - .toLowerCase(); - } + private String normalize(String text) { + String n = java.text.Normalizer.normalize(text, java.text.Normalizer.Form.NFKC); + return n.replaceAll("\\s+", "") // 공백 제거 + .replaceAll("[^가-힣a-zA-Z0-9]", "") // 특수문자 제거 + .toLowerCase(); + }
259-277: 자카드 유사도: 문자 집합보다는 2-그램(바이그램) 기반이 검색 품질에 유리한글/혼합 문자열에서 문자 단위 집합은 구분력이 낮습니다. 2-그램 기반 자카드가 실제 “부분 유사”를 더 잘 반영합니다.
- private double calculateJaccardSimilarity(String s1, String s2) { + private double calculateJaccardSimilarity(String s1, String s2) { if (s1.isEmpty() && s2.isEmpty()) { return 1.0; } if (s1.isEmpty() || s2.isEmpty()) { return 0.0; } - Set<Character> set1 = s1.chars().mapToObj(c -> (char) c).collect(java.util.stream.Collectors.toSet()); - Set<Character> set2 = s2.chars().mapToObj(c -> (char) c).collect(java.util.stream.Collectors.toSet()); + java.util.Set<String> set1 = new java.util.HashSet<>(); + java.util.Set<String> set2 = new java.util.HashSet<>(); + for (int i = 0; i < s1.length() - 1; i++) set1.add(s1.substring(i, i + 2)); + for (int i = 0; i < s2.length() - 1; i++) set2.add(s2.substring(i, i + 2)); + if (set1.isEmpty() && set2.isEmpty()) return 1.0; + if (set1.isEmpty() || set2.isEmpty()) return 0.0; - Set<Character> intersection = new java.util.HashSet<>(set1); + java.util.Set<String> intersection = new java.util.HashSet<>(set1); intersection.retainAll(set2); - Set<Character> union = new java.util.HashSet<>(set1); + java.util.Set<String> union = new java.util.HashSet<>(set1); union.addAll(set2); return (double) intersection.size() / union.size(); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java(3 hunks)src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java(4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (3)
src/main/java/com/wayble/server/explore/service/WaybleZoneSearchService.java (1)
Service(15-49)src/main/java/com/wayble/server/explore/repository/recommend/WaybleZoneQueryRecommendRepository.java (2)
searchPersonalWaybleZones(48-162)Repository(30-163)src/main/java/com/wayble/server/wayblezone/repository/WaybleZoneRepositoryImpl.java (1)
WaybleZoneRepositoryImpl(19-140)
🔇 Additional comments (3)
src/test/java/com/wayble/server/explore/WaybleZoneSearchApiIntegrationTest.java (1)
539-556: 요청-응답 시간 측정 추가: 방향성과 위치 적절함호출 직전에 startTime, 응답 직후 endTime 측정 구간이 적절하며, 성능 회귀 감지에 도움이 됩니다. 유지해도 좋습니다.
src/main/java/com/wayble/server/explore/repository/search/WaybleZoneQuerySearchRepository.java (2)
22-22: Set import 적절자카드 유사도 계산을 위한 Set 사용에 필요한 import 추가, 문제 없습니다.
145-146: distance null 반환 시 서버측 NPE/직렬화 문제 없음—클라이언트 null 처리만 검증 필요
WaybleZoneSearchResponseDto의distance필드는 원시 타입double이 아닌 객체형Double으로 선언되어 있어, 서버에서null을 반환해도 NPE나 직렬화 오류가 발생하지 않습니다.- Jackson 등의 기본 설정에서도
null값을 허용하므로 서버 측에서는 안전합니다.- 프론트엔드 및 기타 소비자 코드에서
distance == null인 경우를 적절히 처리(예: 기본값 대체 또는 null 체크)하고 있는지 한번 더 검증해 주세요.
✔️ 연관 이슈
📝 작업 내용
스크린샷 (선택)
웨이블존 더미 데이터 1000개에 대한 응답 시간 비교입니다. 꽤 유의미한 결과여서 알고리즘을 개선하기로 결정했습니다.
(랜덤 값이어서 두 실험의 위,경도가 다릅니다.)
Summary by CodeRabbit