검색 기능 추가하기

Red_Panda·2021년 4월 6일
0


먼저 검색할 페이지인 question_list에 검색창을 추가했다.

그리고, 같은 템플릿에 page와 kw를 동시에 GET 방식으로 요청하도록 form 엘리먼트를 추가했다. action 속성은 폼이 전송되는 URL이므로 목록 조회 URL을 지정한다.

<a class="page-link" href="?page={{ question_list.prev_num }}">이전</a>

<a class="page-link" data-page="{{ question_list.prev_num }}" href="#">이전</a>

페이징 처리부분을 수정한다. 기존은 href속성에 링크를 직접 입력했지만, 수정된 후에는 사용자의 페이지 동선에 data-page가 저장된다.------


question_list 맨밑에 페이징, 검색 처리 자바스크립트 코드를 추가한다.
class속성이 page-link인 링크를 누르면 data-page 속성값을 읽어 seacrhForm page필드에 그 값을 설정해 폼을 요청한다.
그리고 검색 버튼을 누르면 검색 창에 입력된 값을 searchForm의 kw필드에 설정해 폼을 요청한다. 검색 버튼을 누를경우 처음부터 보여야 하기때문에 page필드에 항상 1을 설정해 요청하도록 했다.

question_views.py의 _list함수를 저번에 배운 쿼리를 이용해 수정한다.

from .. models import Question, Answer, User

먼저 모델들을 import 해준다.

그리고 _list함수를 수정했다. 근데 이렇게 하면 작성자가 비로그인 상태일때의 글들은 검색이 안된다.

비로그인 상태일때도 보이도록 쿼리부분을 어떻게 수정해야할지 공부 해야한다.

전체적인 그림을 그리고 차근차근 하나씩 늘려보기로 했다.

먼저, 검색할때 비로그인상태의 글도 질문제목, 질문내용, 작성자(ip주소)일때도 kw와 일치하면 나와야 한다. 예를들어 127.0.0.1의 행적은 127로 검색시 나와야 한다.

question_list = question_list \
            .join(User)

이 경우에는 로그인한 상태에서 작성한 질문글만 나온다.

question_list = question_list \
            .join(Answer)

답변이 달린 모든 질문글이 나온다. 답변이 없는 질문글은 나오지 않는다.

그럼 모든 질문글이 나와야하니까 이전에 배운 outerjoin을 사용해봤다.

question_list = question_list \
            .outerjoin(Answer).distinct()

답변이 달리지 않는 모든 질문글이 나온다.

question_list = question_list \
            .outerjoin(Answer).distinct() \
            .filter(Question.subject.ilike(search))

이렇게하면 모든 질문글에서 제목에 search 내용의 텍스트가 포함된 질문글들이 나온다.
비 로그인 상태에서 작성한 글도 나온다.

question_list = question_list \
            .outerjoin(Answer).distinct() \
            .outerjoin(User) \
            .filter(Question.subject.ilike(search) |
                    Question.content.ilike(search) |
                    User.username.ilike(search))\
            .distinct()

먼저 Answer를 아우터조인해서 답변이 있는 것들은 Answer 조인하고, 거기서 또 User를 아우터조인해서 User(로그인)상태의 글이라면 User를 조인해줬다.
이렇게하고 kw를 test1으로 검색시 test1이 작성한 질문글, 작성한 답변글, test1이라는 내용이 들어간 질문글이 나온다. 근데 특정 ip주소(비로그인상태)가 작성한 질문, 답변 등을 보려고 127로 검색하면 하나도 안나온다..

question_list = question_list \
            .outerjoin(Answer).distinct() \
            .outerjoin(User) \
            .filter(Question.subject.ilike(search) |
                    Question.content.ilike(search) |
                    User.username.ilike(search) |
                    Answer.ip.ilike(search) |
                    Question.ip.ilike(search))\
            .distinct()

이렇게하고 127검색시 질문글이 모두 나온다. 그래서 생각해보니까, 이전 모델을 구성했을때 질문, 답변 create 에서 g.user(로그인상태)일때도 ip를 저장하도록했다. 그래서 로그인 상태에서도 127.0.0.1이라는 ip가 db에 저장되어 있던 것이다. 그래서 로그인한 상태라면 ip는 저장하지 않도록 수정했다.

모델 수정 후 새로 만든 글들이다. 13번째 글의 경우 비로그인상태에서 댓글을 달았다. 127를 검색했을때 아자아자~ 빼고 잘나온다! 근데 test2로 로그인해서 글을 작성하고, 댓글들을 달아 test2를 검색하니 댓글단 질문글은 나오는데 작성한 질문글이 안나온다.. 근데 test1으로 검색시 test1의 질문 작성글은 나오긴 하는데 model 변경전 글까지만 적용된다.

question_list = question_list \
            .outerjoin(Answer).distinct() \
            .outerjoin(User, User.id == Question.user_id) \
            .filter(Question.subject.ilike(search) | # 질문 제목
                    Question.content.ilike(search) | # 질문 내용
                    User.username.ilike(search) | # 질문 or 답변 작성자
                    Answer.ip.ilike(search) | # 비로그인상태의 답변 작성자
                    Question.ip.ilike(search) | # 비로그인상태의 질문 작성자
                    Answer.content.ilike(search) # 답변 내용
                    )\
            .distinct()

이렇게 하면 test2가 작성한 글, test2 문자가 포함된 질문내용, 답변내용은 나오나, test2가 작성한 답변은 안나온다. User.id == Question.user_id를 User.id == Answer.user_id로 바꾸면 test2가 작성한 글은 안나오나, 포함된 질문, 답변, test2가 작성한 답변은 나온다. 이 둘을 적절히 섞으면 될것같다.


쿼리 초보라. 헤맸다.. 다시 처음부터 어떻게 쿼리를 짜나가야할지 천천히 생각해봤다. 먼저 질문에 답변이 달렸는지, 답변이 달렸다면 답변자가 로그인 상태에서 작성한건지, 그리고 질문자가 로그인상태에서 질문한건지 등 여러 조건을 파악해야한다.

먼저 학교 다닐때 썼던 데이터베이스 교재로 기본 개념과 여러 예시들을 보고 구글링해서 예시들을 다양하게 찾아봤다. 그리고 pybo.db를 SQLite에서 열어, SQL 실행탭으로 어떤식으로 결과가 나올지 여러가지 경우로 실행해봤다.

sub_query = db.session.query(Answer.question_id, Answer.content, User.username) \
            .outerjoin(User, Answer.user_id == User.id).subquery() # 답변자에 대한 user정보 저장 쿼리

아까 위에서 User.id == Question.user_id, User.id == Answer.user_id 둘다 필요했었는데 이 부분을 서브쿼리로 만들어 해결하는거였다.

sub_query = db.session.query(Answer.question_id, User.username) \
            .outerjoin(User, Answer.user_id == User.id).subquery() # 답변자에 대한 user정보 저장 쿼리
        question_list = question_list \
            .outerjoin(Answer) \
            .outerjoin(sub_query) \
            .outerjoin(User, User.id == Question.user_id) \
            .filter(Question.subject.ilike(search) |  # 질문 제목
                    Question.content.ilike(search) |  # 질문 내용
                    User.username.ilike(search) |  # 질문 or 답변 작성자
                    Answer.ip.ilike(search) |  # 비로그인상태의 답변 작성자
                    Question.ip.ilike(search) |  # 비로그인상태의 질문 작성자
                    Answer.content.ilike(search)  | # 답변 내용
                    sub_query.c.username.ilike(search)
                    ) \
            .distinct()

몇 시간 동안 트라이 끝에.. 원하는대로 나온다. 어딘가 빵꾸가 있을지도 모르지만.. 내가 확인한 결과로는 잘 나오니 성공했다.

검색 결과를 위처럼 나오게 하지말고 차라리 네이버카페처럼 제목 찾기, 글 작성자 찾기 등등 검색할 것을 정한뒤에 검색하는것도 나쁘지 않겠다. 지금처럼 결과를 보여주면 어떤사람이 작성한 글을 찾으려고 찾는데, 댓글 단 글까지 모두 보이면 그 안에서 또 작성한 글을 일일이 찾아줘야한다... 좀더 완성도가 높아질때 다시 건드려 봐야 겠다.

profile
신입 개발자

0개의 댓글