Posts nodejs, mongodb 게시판 검색 기능
Post
Cancel

nodejs, mongodb 게시판 검색 기능

목표

게시판 내의 모든 게시물들에 대해 제목, 내용, 제목 + 내용으로 검색할 수 있게 한다.
예를 들어 제목이 ‘helloworld’라는 게시물이 있으면 ‘wor’을 검색해도 나올 수 있게 하는 것이 목표다.

MongoDB의 검색기능 사용

처음 인터넷에서 검색을 해서 찾은 것은 https://sy34.net/mongodb-full-text-search/
이 블로그의 내용을 토대로 코딩을 하기 시작했다. 하지만 이 방법은 적용하는것도 복잡하고 동적으로 검색 방법을 바꾸는 것도 힘들어보였다.
혹시 나중에 이 방법을 쓸 일이 있을 수도 있으니 써본다.

일단 이 방법으로 데이터를 검색하기 위해서는 검색대상의 데이터를 text로 인덱싱해야한다.
예를 들어 posts 스키마가

1
2
3
4
5
{
    "title": <글 제목>,
    "content": <글 내용>,
    "author": <글 작성자>,
}

이런식으로 만들어져 있을 때 내가 글 제목에 대해 검색하려고 하면

1
db.posts.createIndex({"title":"text"})

이런식으로 인덱스를 만들어줘야 검색을 할 수 있게 된다.

위 코드는 mongodb기준의 코드이고
실제로 내가 express와 mongoose를 통해 구현을 할 때는 posts의 스키마를 구성하는 코드 뒤에다

1
postSchema.index({ title: 'text' });

이런식으로 설정을 해주었더니 인덱스가 적용되었다.

이 과정에서 막힌 것이 2가지가 있었는데 한가지는

1
2
postSchema.index({ title: 'text' });
postSchema.index({ content: 'text' });

이런식으로 두 줄로 쓰면 위에있는 title에 대해서만 인덱스가 생성되는 것이었다.

이 문제는 그냥 두 줄의 코드를 합쳐서 해결했다.

1
postSchema.index({ title: 'text' , content: 'text'});

이렇게 작성하면 두개의 값이 인덱싱된다.
인덱스 된 결과가 두개의 인덱스가 합쳐져있어 잘못된 것인줄 알았는데 잘 실행되었다.

검색은 find를 이용해서 $search: "검색할 문자" 옵션을 이용하면 된다.

1
posts.find({$text: {$search: "te"}})

하지만 이렇게 설정하니 두번째로 막히는 부분이 나왔다.
내가 애초에 생각했던 방식은 제목, 내용, 제목+내용 3가지의 방식으로 찾는 것이었는데
인덱스를 설정하는것은 코드를 돌리면서 동적으로 설정했다 풀었다 하기가 힘들어진다는 것이었다.
dropIndex를 사용해서 인덱스를 풀 수는 있지만
인덱스를 설정하는 것은 해당하는 데이터를 균형이진트리로 만드는 것 같은데 이 것을 반복하는 것은
시간복잡도에 상당한 영향을 주는 방법이다.

또한 dropIndex가 코드 상에서 동적으로 작동할 수 있는지도 잘 모르겠다.
아무튼 이 방법 말고 다른 방법이 있나 찾아보았다.

javascript의 정규표현식 이용하기

찾은 해결 방법은 정규표현식을 사용하는 것이다.
자바스크립트의 RegExp()을 사용하여 정규표현식 객체를 생성하고 그 객체로 find를 하면 된다.

예를들어 제목에 test라는 문자열이 들어가는 게시글을 찾으려면

1
Post.find({ title: new RegExp('test')})

이런식으로 코드를 짜면 된다.

앞쪽에서 너무 많이 헤맸는데 생각보다 쉽게 해결되어서 허무했다.

javascript의 정규표현식에 관한 문서
정규표현식을 테스트할 수 있는 사이트

코드

프론트에서 백엔드로 get을 할 때 query값으로 어떤 검색 조건을 선택했는지와
어떤 내용으로 검색을 하는지 각각 title과 content라는 이름으로 넘겨주었다.

검색 조건중에 제목과 내용 두가지 다 검색을 해야하는 경우가 있다.
이 경우를 처리하기 위해서 코드상에 options라는 배열을 만들어
검색옵션과 정규표현식으로 바꾼 내용을 집어넣은 후 find()를 할 때 or옵션을 사용했다.
find({ $or: options }) 이런식으로 하면 or를 통해 제목과 내용 두가지 검색 결과를 얻을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
router.get(
    '/searchpost',
    asyncRoute(async function(req, res) {
        let options = []
        if (req.query.option == 'title') {
            options = [{ title: new RegExp(req.query.content) }]
        } else if (req.query.option == 'content') {
            options = [{ content: new RegExp(req.query.content) }]
        } else if (req.query.option == 'title+content') {
            options = [
                { title: new RegExp(req.query.content) },
                { content: new RegExp(req.query.content) },
            ]
        } else {
            const err = new Error('검색 옵션이 없습니다.')
            err.status = 400
            throw err
        }
        const posts = await Post.find({ $or: options })

전체 코드

https://github.com/Tekiter/EZSET/blob/master/backend/src/api/v1/simple.route.js

nodejs 게시판 조회수 구현

nodejs, mongodb 익명게시판 만들기

Comments powered by Disqus.