저번 글에서 말했던 대로 우리는 음식점을 검색할 수 있는 방법으로
두가지 방법을 구현하기로 했다.
우리가 설정한 필터의 조건은
예를 들어 채식단계에서 아무것도 선택하지 않는다면 모든 채식단계에 대한 식당 목록들을 보여준다.
우선 프론트에서 데이터가 어떻게 넘어오는지 물어봤는데 선택한 항목들을 문자열 그대로 보낼 수도 있고, 항목마다 번호를 붙여 숫자 배열로 넘겨줄 수도 있다고 했다.
처음에는 db에서 LIKE를 사용하여 검색할 생각으로 문자열로 넘겨달라고 했다가, 생각해보니 단순히 문자열만 넘어오면 어디서부터 어디까지가 지역, 채식단계, 음식종류인지 구별하지 못해 검색이 어려워져 int형 배열을 받는 것으로 수정했다.
그 후 controller에 문자열 배열을 만들어 매치하기로 했다.
String resFilterList[]=new String[]{
"강서구", "양천구", "구로구", "금천구", "영등포구", "마포구", "은평구", "서대문구", "동작구",
"관악구", "용산구", "서초구", "강남구", "송파구", "강동구", "중구", "종로구", "성북구", "강북구",
"도봉구", "노원구", "중랑구", "동대문구", "성동구", "광진구",
"비건", "락토", "오보", "락토 오보", "페스코", "폴로", "플렉시테리언",
"한식", "분식", "카페", "베이커리", "양식", "술집", "인도", "중식", "동남아", "일식"};
[ 1, 2, 26, 33 ]
사용자가 다음과 같이 선택했다는 뜻이다.
[ "강서구", "양천구", "비건", "한식" ]
그 후, 카테고리별로 나눠야 하기 때문에 List<String> 3개와 HashSet<Restaurant> 3개를 선언했다.
음식점 정보들을 HashSet 으로 설정한 이유는 카테고리가 3개이고 여러개 선택이 가능하기 때문에 합집합 연산과 교집합 연산이 필요하다고 생각했기 때문이다.
위에서 든 예시로 설명해보면
"강서구", "양천구", "비건", "한식" 을 선택했을 때
1. ( 강서구 음식점들 ) 합집합 ( 양천구 음식점들 )
2. 비건 음식점들
3. 한식 음식점들
을 검색 한 후 1번, 2번, 3번에 대해 교집합 연산을 해줘야 한다.
List<String> resFilterListArea = new ArrayList<>();
List<String> resFilterListLevel = new ArrayList<>();
List<String> resFilterListType = new ArrayList<>();
HashSet<Restaurant> hashListArea=new HashSet<>();
HashSet<Restaurant> hashListLevel=new HashSet<>();
HashSet<Restaurant> hashListType=new HashSet<>();
프론트에서 들어온 정수값들을 분류해주는 작업이다.
정수값이 25보다 작으면 지역에 대한 키워드, 32보다 작으면 비건레벨에 대한 키워드, 42보다 작으면 음식점 타입에 관한 키워드이므로 각각 분류해줬다.
// List<Integer>에 있는 값들 분류해서 String으로 변환
for(Integer i : filteredValue){
if(i <= 24)
resFilterListArea.add(resFilterList[i]);
else if(i <= 31)
resFilterListLevel.add(resFilterList[i]);
else if(i <= 41)
resFilterListType.add(resFilterList[i]);
}
위에서 설명한 대로 만약 선택된 값이 없을 땐 그 카테고리에 대한 모든 정보를 보여주기로 했다.
예를 들어 채식단계에서 아무것도 선택하지 않는다면 모든 채식단계에 대한 식당 목록들을 보여준다.
// List<String> 비어있으면 모든 값 넣기
if(resFilterListArea.isEmpty()){
for(int i=0 ; i<=24 ; i++)
resFilterListArea.add(resFilterList[i]);
}
if(resFilterListLevel.isEmpty()){
for(int i=25 ; i<=31 ; i++)
resFilterListLevel.add(resFilterList[i]);
}
if(resFilterListType.isEmpty()){
for(int i=32 ; i<=41 ; i++)
resFilterListType.add(resFilterList[i]);
}
HashSet의 가장 큰 특징이 중복을 허용하지 않는 것이라는 점을 이용했다.
// List에 있는 값들 하나씩 검색해서 HashSet에 합집합
for(String str : resFilterListArea){
HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterArea(str);
hashListArea.addAll(hashTmp);
}
for(String str : resFilterListLevel){
HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterLevel(str);
hashListLevel.addAll(hashTmp);
}
for(String str : resFilterListType){
HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterType(str);
hashListType.addAll(hashTmp);
}
마지막으로 세 개의 연산 결과에 대해 교집합 처리를 해줬다.
기본으로 제공되는 retainAll() 을 이용하여 동일한 요소는 남기고 나머지는 제거해 주는 연산이다.
// 3개의 HashSet 교집합
hashListArea.retainAll(hashListLevel);
hashListArea.retainAll(hashListType);
return hashListArea;
@Data
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity(name="restaurant")
public class Restaurant {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer resIdx;
private String resName;
private String resAddress;
private String resNum;
private String resCategory;
private String resMenu;
private Integer userIdx;
}
@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/vegan-res")
public class RestaurantJpaController {
@Autowired
private RestaurantRepository restaurantRepository;
// 0 ~ 24 : 지역
// 25 ~ 31 : 비건단계
// 32 ~ 41 : 음식점 타입
String resFilterList[]=new String[]{
"강서구", "양천구", "구로구", "금천구", "영등포구", "마포구", "은평구", "서대문구", "동작구",
"관악구", "용산구", "서초구", "강남구", "송파구", "강동구", "중구", "종로구", "성북구", "강북구",
"도봉구", "노원구", "중랑구", "동대문구", "성동구", "광진구",
"비건", "락토", "오보", "락토 오보", "페스코", "폴로", "플렉시테리언",
"한식", "분식", "카페", "베이커리", "양식", "술집", "인도", "중식", "동남아", "일식"};
// 음식점 필터로 조회
@GetMapping("/restaurant/filter")
public HashSet<Restaurant> searchByFilter(@RequestBody List<Integer> filteredValue){
List<Restaurant> resFilterListFinal=new ArrayList<>();
List<String> resFilterListArea = new ArrayList<>();
List<String> resFilterListLevel = new ArrayList<>();
List<String> resFilterListType = new ArrayList<>();
HashSet<Restaurant> hashListArea=new HashSet<>();
HashSet<Restaurant> hashListLevel=new HashSet<>();
HashSet<Restaurant> hashListType=new HashSet<>();
// List<Integer>에 있는 값들 분류해서 String으로 변환
for(Integer i : filteredValue){
if(i <= 24)
resFilterListArea.add(resFilterList[i]);
else if(i <= 31)
resFilterListLevel.add(resFilterList[i]);
else if(i <= 41)
resFilterListType.add(resFilterList[i]);
}
// List<String> 비어있으면 모든 값 넣기
if(resFilterListArea.isEmpty()){
for(int i=0 ; i<=24 ; i++)
resFilterListArea.add(resFilterList[i]);
}
if(resFilterListLevel.isEmpty()){
for(int i=25 ; i<=31 ; i++)
resFilterListLevel.add(resFilterList[i]);
}
if(resFilterListType.isEmpty()){
for(int i=32 ; i<=41 ; i++)
resFilterListType.add(resFilterList[i]);
}
// List에 있는 값들 하나씩 검색해서 HashSet에 합집합
for(String str : resFilterListArea){
HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterArea(str);
hashListArea.addAll(hashTmp);
}
for(String str : resFilterListLevel){
HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterLevel(str);
hashListLevel.addAll(hashTmp);
}
for(String str : resFilterListType){
HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterType(str);
hashListType.addAll(hashTmp);
}
// 3개의 HashSet 교집합
hashListArea.retainAll(hashListLevel);
hashListArea.retainAll(hashListType);
return hashListArea;
}
}
@Repository
public interface RestaurantRepository extends JpaRepository<Restaurant, Integer> {
// 음식점 필터로 검색 (지역)
@Query("Select r From restaurant r where r.resAddress LIKE %:area%")
HashSet<Restaurant> findRestaurantByFilterArea(@Param("area") String area);
// 음식점 필터로 검색 (비건레벨)
@Query("select r from restaurant r where r.resMenu like %:level%")
HashSet<Restaurant> findRestaurantByFilterLevel(@Param("level") String level);
// 음식점 필터로 검색 (타입)
@Query("select r from restaurant r where r.resCategory like %:type%")
HashSet<Restaurant> findRestaurantByFilterType(@Param("type") String type);
}