일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 도커
- 파이썬
- 운영체제
- Elasticsearch
- 프로그래머스
- 엘라스틱서치
- DPDK
- 카카오
- IT
- 네트워크
- Spring
- 쿠버네티스
- Linux
- 코딩테스트
- 캐시
- programmers
- Kakao
- docker
- 리눅스
- 알고리즘
- 개발자
- Java
- springboot
- 프로그래머스 #카카오 #IT #코딩테스트
- Python
- 자바
- C
- 스프링부트
- 스프링
- 백엔드
- Today
- Total
저고데
[ElasticSearch] 3. 검색 엔진을 구현해보자 본문
이전에 2편에서 관계형 데이터베이스와 엘라스틱 서치에 대한 성능 차이를 실험해보았다.(결과는 내 예상과 매우 달랐지만 ...)
아무튼 아무튼 이번에는 해당 검색 결과를 화면에 출력하는 검색 엔진을 만들어보도록 하겠다.
코드 수정하기
<form th:action="@{/search}" method="get">
<label for="query">검색어:</label>
<input type="text" id="query" name="query" />
<label for="searchType">검색 항목:</label>
<select id="searchType" name="searchType">
<option value="name">이름</option>
<option value="age">나이</option>
</select>
<button type="submit" class="btn btn-primary">검색</button>
</form>
home.html 파일에 다음과 같은 코드를 추가하여 검색 창을 만든다.
이 때, 사용자의 이름과 나이에 대한 정보로 검색이 가능하기 위해서 Thymeleaf의 select를 통해 "이름"과 "나이"를 선택할 수 있게 해준다.
@Repository
public interface UserRepository extends ElasticsearchRepository<User, String> {
List<User> findByName(String name);
@Query("{\"wildcard\":{\"name.keyword\":\"*?0*\"}}")
List<User> findByNameWildcard(String name);
@Query("{\"wildcard\":{\"age.keyword\":\"*?0*\"}}")
List<User> findByAgeWildcard(String age);
}
이는 레포지토리 코드이다.
이전 게시글에서 언급했듯이, @Query 어노테이션 중에서 wildcard를 사용하여 해당 검색어와 완벽하게 일치하는 것만 찾는 것이 아니다.
SQL의 "like" 예약어처럼 해당 검색어가 포함된 모든 결과물을 반환할 수가 있다.
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> findByNameWildcard(String name) {
return userRepository.findByNameWildcard(name);
}
public List<User> findByAgeWildcard(String age) {
return userRepository.findByAgeWildcard(age);
}
}
다음으로 Service 코드이다.
Spring Boot에서 제공하는 ElasticsearchRepository의 자동 메소드를 통해서 접근이 가능하다.
@GetMapping("/search")
public String search(@RequestParam String query, @RequestParam String searchType, Model model) {
if (searchType.equals("name")) {
List<User> users = userService.findByNameWildcard(query);
model.addAttribute("users", users);
} else if (searchType.equals("age")) {
List<User> users = userService.findByAgeWildcard(query);
model.addAttribute("users", users);
}
return "home";
}
그리고 Controller 코드이다.
이전 home.html의 검색 창에서 "이름"과 "나이"를 카테고리로 선택하여 검색이 이루어진다.
따라서 카테고리를 나타내는 searchType을 @RequestParam 어노테이션으로 받아주고 검색어는 역시 같이 받아준다.
그리고 해당 목록들을 List를 통해서 html 화면에 전달한다.
<div th:unless="${users == null or #lists.isEmpty(users)}">
<h2>검색 결과</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.age}"></td>
</tr>
</tbody>
</table>
</div>
<div th:if="${users == null or #lists.isEmpty(users)}">
<p>검색 결과가 없습니다.</p>
</div>
마지막으로 home.html에 다음과 같은 코드를 추가하여 화면에 출력하면 끝이다.
이제 결과를 살펴보자.
주소의 get 요청에서 알 수 있듯이, 이름 카테고리에 temp14라고 검색했을 때의 결과이다.
이름이 temp14로 일치하는 항목만 출력하지 않고 temp141, temp1410처럼 검색어가 포함된 모든 항목들이 포함된 것을 알 수가 있다.
문제 발생 ...
하지만 문제가 발생하였다.
이번에는 temp를 이름 카테고리로 검색을 하였다.
사진에서 알 수 있듯이 에러가 발생하였다.
temp라는 데이터의 갯수가 15000개이기 때문에 한 화면에서 출력할 수 있는 제한량을 초과했기 때문이라고 생각이 든다.
그리고 또 두 번째 문제점이 있었다.
"이름" 카테고리로 검색을 하였을 때는 문제가 없었지만, "나이" 카테고리로 검색을 했을 때, 위의 사진과 동일하게 500에러가 발생하였다.
포스트맨에서 알 수 있듯이 현재 엘라스틱 서치의 user에 저장된 데이터의 나이 타입은 정수형이다.
@GetMapping("/search")
public String search(@RequestParam String query, @RequestParam String searchType, Model model) {
if (searchType.equals("name")) {
List<User> users = userService.findByNameWildcard(query);
model.addAttribute("users", users);
} else if (searchType.equals("age")) {
// List<User> users = userService.findByAgeWildcard(query); -> 기존 코드
List<User> users = userService.findByAgeWildcard(Integer.parseInt(query));
model.addAttribute("users", users);
}
return "home";
}
따라서 다음과 같이 String 형식의 query를 정수형으로 바꿔주었다. (기존 레포지토리, 서비스 코드 역시 변경해주었다.)
하지만 이번에도 ... 계속 500에러가 발생하였다 ..
아마 int형이 아닌 다른 형식이라서 오류가 발생하는 것 같다.
분명 "이름" 카테고리에서 검색을 할 때는 문제가 없는 걸로 보아, 검색 알고리즘 자체는 원인이 아닌 것 같다.
아마 변수의 타입이 원인이지 않나 생각이 든다.
마무리
오늘은 이렇게 엘라스틱 서치를 이용하여 간단한 검색 엔진을 만들어보았다.
다음 시간에는 앞서 발생한 문제 중 하나인 temp를 검색했을 때, 데이터 초과로 발생한 문제를 해결해보도록 하겠다.
Pageable을 사용한다고 다른 분들이 그러시던데 아마 이를 사용할 듯하다.
그리고 다른 타입을 통한 검색도 해결해보도록 하겠다.
'ElasticSearch' 카테고리의 다른 글
[ElasticSearch] 5. 이전 굴욕 만회하기. RDBMS vs ElasticSearch (2) (0) | 2024.03.16 |
---|---|
[ElasticSearch] 4. Spring Page를 통해 검색 결과 페이지로 만들기 (1) | 2024.01.31 |
[ElasticSearch] 2. 관계형 데이터베이스와 엘라스틱 서치 성능 비교하기 (0) | 2024.01.29 |
[ElasticSearch]1. 스프링부트와 엘라스틱 서치 연동하기 (0) | 2024.01.27 |
[ElasticSearch]0. 엘라스틱 서치 왜 배우는가? (1) | 2024.01.25 |