MongoDB를 이용하여 검색 시스템을 만들어보자(: full text search)
반응형

안녕하세요 😉

유유자적한 개발자 유로띠 입니다 😀

 

 

회사에서

프로젝트를 통해 배우고 성장하며

이것을 토대로 그로스 해커(Growth Hacker)가 되기 위한 포스팅입니다.

오늘의 이야기는

다양한 검색어에 대해서

어떻게 전문 검색(full text search)을 지원해야 할지

고민했던 내용입니다 😏 

 

 

 

🤔 전문 검색(full text search)이 필요하다


온라인 강의를 제공하는 서비스 회사에서 

이번 맡은 업무는 강의 검색 고도화입니다.

 

요구사항을 간단히 살펴보겠습니다.

강의 검색 시 제목뿐만 아니라 다음과 같은 범위도 검색의 대상이 되어야 한다.
✅ 강의의 목차
✅ 강사의 이름
✅ 강의 소개
✅ 관련 키워드

 

요구사항을 확인해 보면 결국 단 한 가지! 전문 검색이 가능해야 한다는 점입니다.

 

 

그럼 현재 검색 시스템은 어떤 문제가 있을까요? 🤔

 

 

 

🤔 현재 검색의 문제점

🛑 다양한 검색어에 대해 검색이 불가능하다.

🛑 빈번한 like 검색으로 RDMS에 부담을 줄 수 있다.

🛑 like (%%) 검색의 경우 인덱스를 사용하지 않아 full scan이 발생된다.

🛑 text 기반으로 검색되기 때문에 유의어, 동의어 등의 검색이 불가능하다. (ex. 파이선, 파이썬)

 

 

아마 별도의 검색 엔진이 없다면 저희와 비슷한 이슈가 있을 것 같습니다.

 

그럼 어떻게 해야 할까요?...

 

 

🚀 GROW UP POINT

전문 검색이 가능한 검색 엔진을 구성하여 단독 검색 서비스를 만들어 사용하자

 

 

 

✅ 검색 서비스의 필요성

🔵 비정형 데이터의 색인, 검색이 가능합니다.

🔵 형태소 분석을 통해 자연어 처리가 가능합니다.

🔵 역 색인(역 인덱스)으로 빠른 검색이 가능합니다.

🔵 기존 RDBMS와 분리하여 검색 정보를 MongoDB에 저장하기 때문에 RDBMS의 부하를 줄일 수 있습니다.

 

 

 

✅ 검색 시스템의 아키텍처

검색 서비스가 왜 필요한지 알았으니, 검색 아키텍처를 설계해 보겠습니다. 😏

환경 구성은 다음과 같습니다.

🟡 MongoDB Atlas

🟡 cloud run

🟡 RDBMS (Maria DB)

 

서버의 경우 현재 회사에서 사용 중인 프레임워크와 언어를 이용하여 빠르게 만들기 위해 다음과 같이 사용하였습니다.

🟡 fastify

🟡 typeorm

🟡 typescript

 

 

검색 시스템 아키텍처를 6단계인 크롤링, 저장, 인덱스, 검색, 스코어링, 결과 표시로 설계하였습니다.

 

🙋‍♂️ 크롤링

검색할 대상 문서를 가져와야 합니다. 검색할 대상 문서는 강의 정보가 저장된 RDBMS에서 가져옵니다. 여기서 강의 제목, 강사 이름, 강의 소개 정보를 가져오고 목차의 경우 목차 전체를 연결하여 하나의 필드에 추가합니다.

 

🙋‍♂️ 저장

검색 타겟인 MongoDB에 데이터를 저장합니다.

저장되는 스키마는 다음과 같이 간단하게 구성하였습니다.

 

export const courseSchema = new Schema(
  {
    courseId: { type: Number, default: 0, index: true, description: '강의 아이디' },
    state: { type: String, default: 'NORMAL', required: true, description: '상태' },
    content: { type: String, default: null, required: false, description: '콘텐츠' },
    title: { type: String, default: null, required: false, description: '강의제목' },
    instructorName: { type: String, default: null, required: false, description: '강사 이름' },
  },
  { timestamps: true }
);

 

🙋‍♂️ 인덱싱

mongoDB의 n-gram을 이용하는 역 색인(인덱스)을 구축합니다. 

역 색인 생성 방법은 저번 블로그에 작성하였습니다.

2023.07.08 - [Programming/mongoDB] - MongoDB에서 nGram 사용하기(fts, search index)

 

MongoDB에서 nGram 사용하기(fts, search index)

안녕하세요 😉 유유자적한 개발자 유로띠 입니다 😀 👏👏👏👏 이번 포스팅에서는 ✅ n-gram 이란? ✅ search Index 설정하기 에 대해서 알아보겠습니다 n-gram 이란? ✅ 전문 검색의 종류 n-gram을 설

msyu1207.tistory.com

 

🙋‍♂️ 검색

드디어 사용자가 검색 쿼리를 통해 검색을 하고 결과가 포함된 데이터를 반환합니다.

 

mongoDB에서 생성한 search_index를 index에 넣고 검색의 대상이 되는 필드를 path에 작성합니다.

이후 사용자가 입력한 query가 입력되면 아래의 aggregate를 통해 검색이 진행되고 검색의 결과는

[{ courseId, score } ]의 형태로 배열로 전달합니다.

const query = '검색';

const aggregate = [
        {
          $search: {
            index: 'search_index',
            compound: {
              autocomplete: {
                query,
                path: 'content',
              },
            },
          },
        },
        {
          $project: { _id: 0, courseId: 1, score: { $meta: 'searchScore' } },
        },
        {
          $facet: {
            docs: [{ $limit: limit }],
            meta: [{ $replaceWith: '$$SEARCH_META' }, { $limit: 1 }],
          },
        },
      ];

 

🙋‍♂️ 스코어링

검색 결과의 노출 순서를 스코어링을 통해 전달합니다. 스코어를 산출하는 공식은 아래의 공식문서에 나와있습니다.

또한 원하는 곳에 가중치를 부여할 수 도 있습니다.

 

Return the Score Details — MongoDB Atlas

Docs Home → MongoDB Atlas You can use the scoreDetails boolean option in your $search stage for a detailed breakdown of the score for each document in the query results. To view the metadata, you must use the $meta expression in the $project stage.{ "$se

www.mongodb.com

 

🙋‍♂️ 결과

최종적으로 결과를 사용자에게 전달합니다.

 

 

최종적으로 6가지의 검색 엔진 구현 단계를 다음 그림으로 표현해 보았습니다.

 

 

이번 프로젝트를 통해 개선하거나 변경된 점 👍


 

다양한 검색어에 대해 검색이 불가능하다.

🔜  n-gram으로 검색하기 때문에 전문 검색이 가능해졌습니다.

 

빈번한 like 검색으로 RDMS에 부담을 줄 수 있다.

like (%%) 검색의 경우 인덱스를 사용하지 않아 full scan이 발생된다.

🔜  별도의 api 서버로 분리하였기 때문에 RDMS의 부하를 줄이고 반환되는 결과를 강의 아이디로 전달하기 때문에 이후 ids로 강의를 조회하여 결과를 전달하면 됩니다.

 

text 기반으로 검색되기 때문에 유의어, 동의어 등의 검색이 불가능하다. (ex. 파이선, 파이썬)

🔜  nori라는 형태소 분석기를 사용하기 때문에 유의어, 동의어 등 검색이 가능해졌습니다. 또한 불필요한 조사를 삭제하고 검색하기 때문에 보다 정확하게 결과를 전달합니다.

 

검색에 대한 우선순위가 없다

🔜  검색어에 대해서 얼마나 일치하는지 전문 검색을 통해 스코어링을 하기 때문에 노출 정확도에 따라 순서를 정렬하여 전달할 수가 있습니다.

 

 

 

👀 마무리


전문 검색이 가능한 단독 api 서버를 생성하여 RDBMS의 부담을 줄이고 확장성이 용이한 통합 검색이 되도록 구성하였습니다.

또한 하나의 마이크로 서비스를 기획부터 아키텍처 설계, 개발, 구현, 배포까지 전부 작업함으로써

많은 공부가 되었고 MongoDB의 활용과 n-gram에 대해 전보다 더 많은 기능과 내용을 알게 되었습니다.

 

 

 

참고

 

mongoDB atlas

https://www.mongodb.com/docs/atlas/atlas-search/tutorial/

 

Get Started with Atlas Search — MongoDB Atlas

Docs Home → MongoDB Atlas This tutorial takes you through the steps of setting up and querying an Atlas Search index. You will use a collection with movie data from the Atlas sample data set.To complete this tutorial you will need:The following table sho

www.mongodb.com

 

 

fastify-typescript-mongo로 만든 템플릿입니다.

https://github.com/alstjs1207/fastify-typescript-mongo

 

GitHub - alstjs1207/fastify-typescript-mongo: starter kit for fastify-typescript-mongo

starter kit for fastify-typescript-mongo. Contribute to alstjs1207/fastify-typescript-mongo development by creating an account on GitHub.

github.com

 

 

반응형