QueryDSL을 이용한 다중 검색 조건

김정훈·2023년 8월 21일
0

서론

이 프로젝트의 검색 카테고리는 field 영역과 language 영역으로 나누어져 있다.
간단한 검색창이라면 검색창에 검색어를 입력하여


select * from table where Like "%검색어%"

이런 방법으로 처리를 할텐데 내 프로젝트의 검색 방식은

분야 = [프론트엔드,백엔드], 언어 = [PYTHON] 이렇게 검색 결과가 저장되고

엔티티를 세분화 하여

Project

Field_Project

Language_Project

이렇게 구성했기 때문에 다른 방식으로 접근해야 했다.

본론

1. eqAll(languageList) And eqAll(fieldList)
  • 단순하게 생각했다.
    앞서 봤듯이 project의 카테고리를 별개의 엔티티로 따로 관리하기 때문에
    project.language와 같은 방법을 사용할 수 없다.
        Language_Project 테이블
            Field_Project의 테이블

사용자의 검색 요청을 ListlanguageList, ListfieldList에 저장
-> languageList,fieldList와 모두 일치하는 project_id만 검색

  • 장점

    1. 쿼리문이 간단하다.
  • 단점

    1. Project 테이블을 join 하지 않았기 때문에 project_id를 이용해서 Project를 또 select 해야한다.
    2. querydsl에서 eqAll()에 들어갈 변수는 CollectionExpressions 혹은 SubqueryExpressions타입이
      되어야 하는데 java의 ArrayList는 캐스팅을 하면 에러가 발생했다.
  • eqAll()의 에러 코드

    즉 eqAll이 아닌 다른 방식을 생각해야 한다.

``
2. join 활용

  • eqAll()이 아니라면 In을 사용할 수 밖에 없는데 In(List)는 List 내부의 요소 중 한 요소만
    true여도 select 하기 때문에 문제가 된다.

  • 두 테이블을 join한 후 group having의 count()를 활용하여 count(List.size())를 한다면 특정 projectId에 해당하는 필드의 Language (Field도 마찬가지)가 List중 하나와 일치해야 하고 이게 List.size()만큼을 만족해야 한다면 All과 동일한 효과를 볼 수 있다고 생각했다.

    ex)
    LanguageList=['C','Java']이고 project_id=3을 찾는다고 하면

    		 QueryResults<Project> projectQueryResults = queryFactory.select(project)
                       .from(language_Project)
                       .where(language_Project.language.in(languageList))
                       .groupBy(project)
                       .having(project.count().eq((long) languageSize))
                       .fetchResults();
                       

    language.size()=2이기 때문에 project_id=3이면서 language가 C 혹은 JAVA를 만족하는 필드가 2개를 만족한다면 해당 필드 값을 만족하는 project를 결과로 보여주게 된다.

    count()방식은 어디까지나 한 project_id에 같은 language가 중복되지 않기 때문에 가능하다.

    mysql에 member로 작성해 봤다.

    member 객체 자체는 가져올 수 없었지만 member.id는 가져올 수 있었다.

  • query와 java를 모두 활용하기

    우선 앞서 작성했다 sql 처럼 서브쿼리를 이용하여 Project, Language, Field를 조인한 결과를 한번에 가져오고 싶었지만 나의 능력부족으로 구현하지 못했다.

    고민하다가 Project ⋈ Language의 결과와 Project ⋈ Field의 결과를 따로 가져와서 결과값이 같은 객체만 분류해서 새로운 List에 담아두기로 했다.

    1. Language 혹은 Field만을 검색 요청할 때
      이 때는 join을 한 번만 하면 되기 때문에 별 문제가 없다.
      위에서 보여준 quertdsl의 query와 동일하다.

    2. Language 와 Field 모두를 검색 요청할 때
      - 우선 1번처럼 각각의 결과를 가져온다.
      - 각 결과를 비교하여 같은 객체를 따로 저장한다.

            
           <!-- Empty 체크는 Language, Field 중 하나라도 결과가 없다면 작동을 멈추게 하기 위함이다.-->
                  if (!field_searchList.isEmpty()) {
                  <!--편의를 위해 Language ⋈ Project 결과는 project.id만 가져옴 -->
                      for (Long l : languageQueryResults) {
                          for (Project f : field_searchList) {
                          <!-- id가 일치한다. 즉 결과값이 같다-->
                              if (l.equals(f.getId())) {
                                  tempProjectList.add(f);
                                  break;
                              }
      
                          }
                      }
                  }
      		
  • Field= ['프론트엔드, '백엔드'] , Language=['C','JAVA'] 결과
    - sql 결과

    - query 결과

    - 화면 결과

결론

  • 한 카테고리에 값을 중복하여 검색하기 위해 query와 java를 둘 다 이용했다.
  • 성능 부분에서는 상당히 아쉬울 것 같다.
  • 백엔드 공부를 하면서 쿼리문 작성이 정말 어렵다는걸 느끼고 있다.
profile
백엔드 개발자

0개의 댓글