Elastic search 자연어 검색 (1)

꽉오·2021년 12월 20일
0

** 이 자료는 개인 공부용이고, 정답이 아니니 참고하지 않는 것이 더 좋습니다.

요구사항

  1. 전체 이벤트에 대한 제목, 이벤트 유형, 카테고리, 가격 등 여러 필드 검색이 가능해야함
  2. 동일한 내용으로 호스트 검색도 같이 되어야함
  3. 여러 질의와 검색어에 대해, 우선순위가 있어야함

생각해본 것

  1. SQL like, where 을 사용.
  2. 검색엔진을 직접 만든다.
  3. Elastic search 를 사용.

1차 시도 - SQL

select * from "Events" "event" where name like '%네이버%'

name 단일 검색 자체는 괜찮음. -> 100ms 이하

select * from "Events" "event"
    inner join "EventMetadata" EM on event."eventId" = EM."eventId"
    inner join "Data" D on EM."dataId" = D."dataId" and EM.name = 'contents'
where event.name like '%네이버%'
  and D.data like '%네이버%'
  • 이벤트 내용에 대한 질의만 넣으면 -> 400ms 이상
    조건 추가하면 1s 까지도 걸릴 수 있었음

  • 인스턴스 자체 성능을 늘려봤을땐 괜찮았지만, 계속된 검색은 견디기 어려워 했음

  • 자연어 검색의 경우, index 또한 무의미했음
    ngram 은 Postgres 에서 지원하나, 한글에 적용시키는 것은 너무 어려움

2차 시도 - 검색엔진 개발

팀원중 하나가 말해서 생각해봤지만, 바로 기각
님이 만들면 쓰겠습니다

3차 시도 - Elastic search

index

GET /production_events
{
  "production_events" : {
    "aliases" : { },
    "mappings" : {
        "properties" : {
            "name" : {
                "type" : "text",
                "fields" : {
                    "keyword" : {
                        "type" : "keyword",
                        "ignore_above" : 256
                    },

마치 DB 의 테이블 느낌 (예전엔 인덱스 이전에 구분가능한게 하나 더 있었는데, 7 버전부터 사라짐)

mapping.properties 각 필드별 맵핑 값들

mapping.properties.${field_name}.type 데이터의 타입, 모든 필드는 따로 맵핑 테이블을 지정하지 않으면, text 타입이 됨

mapping.properties.${field_name}.fields 추가 타입 지정 가능 -> ES 는 이 타입 기준으로 인덱싱을 추가적으로 진행함

String type 지정 가능한 것들

text

문자열이 들어오면, 그 값을 텀 단위로 쪼개놓음 -> 형태소 단위로 쪼개놔서, 단어 기준으로 전문 검색이 가능 / 단 기본설정으로 한글을 제대로 쪼개질 못하고, 네이버 만 들어와도 검색 안됨.

keyword

문자열 전체 값을 저장해놔서, 완전히 모든 문장이 동일하면 검색됨

Ngram

애널라이저 / 토크나이저를 지정하면 사용할 수 있다. ES 에선 단어를 n 개씩 쪼갬
만약 단어 묶음을 사용하고 싶으면, shingle 이라는 기능을 사용하면 된다.

min_gram - 최소 단어 수
max_gram - 최대 단어 수

텍스트가 들어오면 Analyzer 가 받아서, 이 데이터를 분석한다
분석시 어떤 Tokenizer 를 사용할지 설정되어 있어야, 문장 분석을 할 수 있다.
우리는 커스텀 애널라이저에 토크나이저를 ngram 을 사용함

"analysis" : {
    "analyzer" : {
        "ngram_analyzer" : {
            "filter" : [ "lowercase", "trim" ],
            "type" : "custom",
            "tokenizer" : "ngram_tokenizer"
        }
    },
    "tokenizer" : {
        "ngram_tokenizer" : {
            "token_chars" : [ "letter", "digit", "punctuation", "symbol" ],
            "min_gram" : "1",
            "type" : "ngram",
            "max_gram" : "5"
        }
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "name.keyword": "네이버"
          }
        },
        {
          "match": {
            "name.ngram": "네이버"
          }
        },
        {
          "match": {
            "name": "네이버"
          }
        }
      ]
    }
  }
}

should 키워드 안에 넣어버리면, 각 매치된 Score 또한 확인이 가능하며 order 도 스코어 기준으로 가능함. -> 여러 필드 중 가장 높은 값에 매칭되면 그것을 최상단으로

이런식으로 이용하면, 각각의 매치 결과를 적절히 잘 배합해서 보여줄 수 있다.

각각 조건 또한 term, match 잘 조합해서 보여줄 수 있음

문자열 검색 키워드

  1. term
    순서 상관 없이, 순수하게 토큰만 맞으면 가져옴 -> tag 검색같은거 쓰면 좋을 듯

  2. match
    애널라이즈 된 데이터를 기준으로 검색을 함.
    ~> 순서 상관 없이, 토큰 중 하나라도 일치하면 결과에 포함된다.

  3. match_pharse
    match 와 동일하나, 순서도 맞아야함 -> 전문검색에 좋을 듯

일단 당장은 match 만 사용하고, 전문검색은 임시로 제거한 상태

0개의 댓글