- SearchController: q 빈값 가드 (HTTP 400) — '%%' LIKE 응답 폭발 차단
- SearchService:
- keywordSearch: LIKE 와일드카드 escape (%, _, \\)
- hybrid 모드: semantic 결과에도 attachChannels 호출 (이전: keyword만)
- ObjectMapper/TypeReference static 재사용 (캐시 hit 경로 GC 압박 완화)
- 알 수 없는 mode → warn 로그 + keyword fallback (이전: silent)
- maxDistance를 @Value("${app.search.max-distance:0.57}")로 외부화
- SearchMapper.xml: LIKE 절에 ESCAPE '\\' 추가
- VectorService.searchSimilar: embeddings/first list null/empty 가드 (NPE 방지)
- application.yml: app.search.max-distance (env SEARCH_MAX_DISTANCE) 추가
후속 분리: batch insert + 테스트 (별도 후속 이슈)
Refs: #293
35 lines
1.1 KiB
Java
35 lines
1.1 KiB
Java
package com.tasteby.controller;
|
|
|
|
import com.tasteby.domain.Restaurant;
|
|
import com.tasteby.service.SearchService;
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.server.ResponseStatusException;
|
|
|
|
import java.util.List;
|
|
|
|
@RestController
|
|
@RequestMapping("/api/search")
|
|
public class SearchController {
|
|
|
|
private final SearchService searchService;
|
|
|
|
public SearchController(SearchService searchService) {
|
|
this.searchService = searchService;
|
|
}
|
|
|
|
@GetMapping
|
|
public List<Restaurant> search(
|
|
@RequestParam String q,
|
|
@RequestParam(defaultValue = "keyword") String mode,
|
|
@RequestParam(defaultValue = "20") int limit) {
|
|
// #293 — q 빈값 가드: '%%' LIKE로 응답 폭발 차단
|
|
if (q == null || q.isBlank()) {
|
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "검색어가 필요합니다");
|
|
}
|
|
if (limit > 100) limit = 100;
|
|
if (limit < 1) limit = 1;
|
|
return searchService.search(q.trim(), mode, limit);
|
|
}
|
|
}
|