본문 바로가기

Backend Study/ELK Stack

[Elastic Search] 데이터 모델링

엘라스틱서치에서는 색인할 때 문서의 데이터 유형에 따라 필드에 적절한 데이터 타입을 지정해야한다. 이러한 과정을 매핑이라하며, 매핑은 색인된 문서의 데이터 모델링이라고도 할 수 있다. 이는 관계형 데이터베이스에서 ERD 설계만큼 매우 중요한 과정이다! 

 

조금 더 구체적으로 설명하면, 매핑은 색인 시 데이터가 어디에 어떻게 저장될지를 결정하는 설정이다. 데이터베이스의 저장소로 볼 수 있는 인덱스에 데이터가 추가될 때 그 데이터의 타입을 구체적으로 정의하는 일이다. 

 

스럽 서비스에서 가장 간단한 인덱스를 예시로 들어 설명할테니 차근 차근 이해해보자.

1.  매핑 인덱스 만들기  

매핑 정보를 설정할 때는 다음과 같은 사항을 고민해야 한다. 

 

- 문자열을 분석할 것인가?
- _source에 어떤 필드를 정의할 것인가?
- 날짜 필드를 가지는 필드는 무엇인가?
- 매핑에 정의되지 않고 유입되는 필드는 어떻게 처리할 것인가? 

 

필드의 데이터 타입은 아래와 같다. 

 

문자열 데이터 타입: 실제 검색 대상이 되는 필드는 text 타입으로 정의하고, 해당 정보를 그대로 보여주기만 하면 되는 대상은 keyword로 정의한다. 

일반적 데이터 타입: date, long, double, integer, boolean, ip

Json 타입: 객체 또는 중첩문과 같은 특성을 가지는 타입 

특수 데이터 타입: geo_point, geo_shape

 

가장 생소한 부분이 Keyword, Text  일 것이다. 구분해서 알고있어야한다! 

 

- Keyword

말 그대로 키워드 형태로 사용할 데이터에 적합한 데이터 타입이다. keyword 타입을 사용할 경우 별도의 분석기를 거치지 않고 원문 그대로 색인하기 때문에 특정 코드나 키워드 등 정형화된 콘텐츠에 주로 사용한다.  만약 elastic search라는 문자열이 keyword 타입으로 설정되면 'elastic'이나 'search'라는 질의로는 절대로 검색되지 않는다. 

 

ex. 검색 시 필터링되는 항목, 정렬이 필요한 항목, 집계해야하는 항목 등에 많이 사용한다. 

 

- Text

Text 데이터 타입을 이용하면 색인 시 지정된 분석기가 칼럼에 데이터를 문자열로 인식하고 이를 분석한다. Text 데이터 타입은 전문 검색이 가능하다는 점이 가장 큰 특징이다.

Text 타입으로 데이터를 색인하면 전체 텍스트가 토큰화되어 생성되며 특정 단어를 검색하는 것이 가능해진다. 

 

적용하기

@Field(type = FieldType.Integer)
private String user_id;

@Field(type = FieldType.Text)
private String nickname;

@Field(type = FieldType.Text)
private String nickname_ngram;

@Field(type = FieldType.Text)
private String nickname_kr_eng2kor;

@Field(type = FieldType.Text)
private String nickname_kr_chosung;

@Field(type = FieldType.Text)
private String nickname_kr_jamo;

@Field(type = FieldType.Date)
private String created_at;

@Field(type = FieldType.Keyword)
private String user_status;
​

 

스럽 서비스에서 실제 사용중인 유저 정보를 저장하고 있는 인덱스의 매핑정보이다.

user_id 필드는 pk값으로 사용 중인 자동 증가값이기 때문에 Integer로 설정했고, nickname과 관련된 필드는 분석 시 사용되기 때문에 Text 타입으로 설정했다. (ngram, chosung 등이 붙은 칼럼은 뒷 부분에서 설명할 예정이니 일단 타입만 보자 ! )

데이터 생성 날짜인 created_at 필드는 date 타입으로, 분석이 필요 없는 user_status 필드는 keyword 타입으로 설정했다. 

 

2.  매핑 파라미터 

엘라스틱서치는 매핑 파라미터에 색인할 필드의 데이터를 어떻게 저장할지에 대한 다양한 옵션을 제공한다. 위에서 만든 매핑 인덱스에 적용해보자. 

 

- analyzer 

해당 필드의 데이터를 형태소 분석하겠다는 의미의 파라미터이다. text 데이터 타입의 필드는 analyzer 매핑 파라미터를 기본적으로 사용해야한다. 별도로 분석기를 지정하지 않으면 Standard Analyzer 형태소 분석을 수행한다.

 

- normalizer

term query에 분석기를 사용하기 위해 사용된다. 예를 들어 keyword 데이터 타입의 경우 원문을 기준으로 문서가 색인되기 때문에 cafe, Cafe가 다른 문서로 인식된다. 하지만 이 유형을 normalizer를 통해 분석기에 필터를 사용하면 같은 데이터로 인식한다. 

 

- boost 

드에 가중치를 부여한다. 가중치에 따라 유사도 점수가 달라지기 때문에 boost설정 시 검색 결과의 노출 순서에 영향을 준다. 7.0.0 버전부터 색인 시점에서는 사용할 수 없고, 검색 시점에서 사용가능하다. 

 

- coerce

색인 시 자동 변환을 허용할지 여부를 설정하는 파라미터다. 예를 들어 "10"과 같은 숫자 형태의 문자열이 integer 타입의 필드에 들어온다면 엘라스틱서치는 자동으로 형변환을 수행해서 정상적으로 처리한다. 

 

- copy to 

매핑 파라미터를 추가한 필드의 값을 지정한 필드로 복사한다. 예컨대 keyword 타입의 필드에 copy_to 매핑 파라미터를 사용해 다른 필드로 값을 복사하면 복사된 필드에서는 text 타입을 지정해 형태소 분석을 할 수도 있다. 

 

- search_analyzer

일반적으로 색인과 검색 시 같은 분석기를 사용한다. 만약 다른 분석기를 사용하고 싶은 경우 search_analyzer를 설정해서 검색 시 사용할 분석기를 별도로 지정할 수 있다.

 

그 외에도 많은 유용한 파라미터들이 있으니 필요하면 검색해서 찾아보자 . 

 

참고 링크

https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-params.html

 

Mapping parameters | Elasticsearch Guide [8.7] | Elastic

 

www.elastic.co

 

 

 

적용하기

"properties" : {
  "nickname": {
    "type": "text",
    "copy_to": ["nickname_ngram", "nickname_kr_eng2kor", "nickname_kr_chosung", "nickname_kr_jamo"]
  },
  "created_at": {
    "type": "date"
  },
  "user_status": {
    "type": "keyword"
  },
  "user_id": {
    "type": "integer"
  },

  "nickname_ngram": {
    "type": "text",
    "analyzer": "ngram_analyzer",
    "search_analyzer": "whitespace"
  },
  "nickname_kr_eng2kor": {
    "type": "text",
    "analyzer": "ngram_analyzer",
    "search_analyzer": "eng2kor_analyzer"
  },
  "nickname_kr_chosung": {
    "type": "text",
    "analyzer": "chosung_index_analyzer",
    "search_analyzer": "whitespace"
  },
  "nickname_kr_jamo": {
    "type": "text",
    "analyzer": "jamo_index_analyzer",
    "search_analyzer": "jamo_search_analyzer"
  }
}

nickname 필드에서는 copy_to 파라미터를 사용하여 nickname_ngram, nickname_kr_eng2kor 등의 필드로 복사하고 있다. 

nickname_ngram, nickname_kr_eng2kor 등의 필드에서는 analyzer로 인덱싱 할 때 사용할 분석기와, search_analyzer로 검색시 사용할 분석기를 따로 정의하고 있다. 

 

3.  엘라스틱서치 분석기

엘라스틱서치는 문서를 색인하기 전에 해당 문서의 필드 타입이 무엇인지 확인하고 텍스트 타입이면 분석기를 이용해 이를 분석한다. 텍스트가 분석되면 개별 텀으로 나뉘어 형태소 형태로 분석된다. 해당 형태소는 특정 원칙에 의해 필터링되어 단어가 삭제되거나 추가, 수정되고 최종적으로 역색인된다. 어떤 분석기를 사용하느냐에 따라 분리되는 결과도 달라진다. 당연히 언어별로 조금씩 다르게 동작하고, 엘라스틱서치를 언어별 분석기를 제공하고있다. 

 

분석기는 기본적으로 다음과 같은 프로세스로 동작한다. 

1. 문장을 특정한 규칙에 의해 수정한다.
2. 수정한 문장을 개별 토큰으로 분리한다.
3. 개별 토큰을 특정한 규칙에 의해 변경한다. 

이 세가지 동작은 특성에 의해 각각 다음과 같은 용어로 불린다. 

 

-character filter

텍스트를 개별 토큰화하기 전의 전처리 과정이다. 문장을 분석하기 전에 입력 텍스트에 대해 특정한 단어를 변경하거나 HTML과 같은 태그를 제거하는 역할을 하는 필터다.

 

Html strip char: 

문장에서 HTML을 제거하는 필터이다. 

 

- tokenizer filter

텍스트를 어떻게 나눌 것인지 정의한다. 한글을 분해할 때는 한글 형태소 분석기의 tokenizer를 사용하고, 영문을 분석할 때는 영문 형태소 분석기의 tokenizer를 사용하는 등 상황에 맞게 적절한 토크나이저를 선택한다. 

 

Standard 토크나이저:

엘라스틱 서치에서 일반적으로 사용한느 토크나이저로 대부분의 기호를 만나면 토큰으로 나눈다. 파라미터로는 max_token_length를 가지고, 최대 토큰 길이를 초과하는 경우 해당 간격으로 토큰을 분할한다. (default는 255)

 

Whitespace 토크나이저:

공백을 만나면 텍스트를 토큰화한다. standard 토크나이저와 동일하게 max_token_length를 파라미터로 가진다. 

 

Ngram 토크나이저: 

기본적으로 한 글자씩 토큰화한다. Ngram에 특정 문자를 지정할 수도 있으며, 이 경우 지정된 문자의 목록 중 하나를 만날 때마다 단어를 자른다. 파라미터 min_gram, max_gram을 가지며 각각 Ngram을 적용할 문자의 최소, 최대 길이를 나타낸다. token_chars라는 파라미터도 있는데, 토큰에 포함할 문자열을 지정한다. (letter, digit, whitespace 등 )

 

ex. min_gram: 3, max_gram: 3으로 저징했을 때 text가 "Harry Potter and the Chamber of Secrets" 라고 들어오면 결과는 [Har, arr, rry, Pot, ott, tte, ter, and ..] 가 된다. 

 

Edge Ngram 토크나이저:

지정된 문자의 목록 중 하나를 만날 때마다 시작 부분을 고정시켜 단어를 자르는 방식으로 사용하는 토크나이저다. Ngram에 특정 문자를 지정할 수도 있으며, 이 경우 지정된 문자의 목록 중 하나를 만날 때마다 단어를 자른다. 파라미터 min_gram, max_gram을 가지며 각각 Ngram을 적용할 문자의 최소, 최대 길이를 나타낸다. token_chars라는 파라미터도 있는데, 토큰에 포함할 문자열을 지정한다. (letter, digit, whitespace 등 )

 

ex. min_gram: 2. max_gram: 10으로 저장했을 때 text가 "Harry Potter and the Chamber of Secrets" 으로 들어오면 결과는 [Ha, Har, Harr, Harry, Po, Pot, Pott, Potte...] 가 된다. 

 

- token filter

토큰화된 단어를 하나씩 필터링해서 사용자가 원하는 토큰으로 변환한다. 예를 들어, 불필요한 단어를 제거하거나 동의어 사전을 만들어 단어를 추가하거나 영문 단어를 소문자로 변환하는 등의 작업을 진행할 수 있다. 

토크나이저에 의해 토큰이 모두 분리돼야 비로소 동작하기 때문에 독립적으로 사용할 수 없다. 

 

Lowercase, Uppercase 토큰 필터:

토큰을 구성하는 전체 문자열을 소문자, 대문자로 변환한다. 

 

Trim 토큰 필터:

앞뒤 공백을 제거하는 토큰 필터다.

 

 

분석기 종류 참고 링크

 

[Elasticsearch] - 4. 엘라스틱서치 분석기

안녕하세요. 개발개입니다. 위키북스에서 나온 를 공부하면서 정리한 부분을 다음과 같은 목차에 따라 공유합니다. (설치와 관련된 포스트는 많기 때문에 따로 작성하지 않았습니다.) 엘라스틱

the-dev.tistory.com

 

간단한 예시 

<B> Elastic search </B> is cool -> Elasticsearch is cool -> Elasicsearch / is / cool -> elasticsearch / is / cool

 

적용하기

 

{
  "index" : {
    "analysis" : {
      "analyzer" : {
        "kor2eng_analyzer" : {
          "type" : "custom",
          "tokenizer" : "standard",
          "filter" : ["trim", "lowercase", "javacafe_kor2eng"]
        },
        "eng2kor_analyzer" : {
          "type" : "custom",
          "tokenizer" : "standard",
          "filter" : [ "trim", "lowercase", "javacafe_eng2kor"]
        },
        "chosung_index_analyzer" : {
          "type": "custom",
          "tokenizer" : "ngram_tokenizer",
          "filter" : [ "javacafe_chosung_filter", "lowercase", "trim", "edge_ngram_filter_front"]
        },
        "chosung_search_analyzer" : {
          "type" : "custom",
          "tokenizer" : "keyword",
          "filter" : [ "javacafe_chosung_filter", "lowercase", "trim"]
        },
        "ngram_analyzer" : {
          "type" : "custom",
          "min_gram": "1",
          "max_gram": "3",
          "tokenizer" : "ngram_tokenizer",
          "filter" : [
            "lowercase", "trim"
          ]
        },
        "jamo_index_analyzer": {
          "type" : "custom",
          "tokenizer" : "ngram_tokenizer",
          "filter" : [
            "javacafe_jamo_filter", "lowercase", "trim", "edge_ngram_filter_front"
          ]
        },
        "jamo_search_analyzer" : {
          "type" : "custom",
          "tokenizer" : "ngram_tokenizer",
          "filter" : [
            "javacafe_jamo_filter", "lowercase", "trim"
          ]
        },
        "html_analyzer" : {
          "type" : "custom",
          "tokenizer" : "standard",
          "filter" : [ "lowercase" ],
          "char_filter" : [ "html_strip" ]
        }
      },

      "tokenizer" : {
        "ngram_tokenizer" : {
          "type" : "nGram",
          "min_gram" : "1",
          "max_gram" : "3",
          "decompound_mode" : "mixed",
          "token_chars" : [ "letter", "digit", "punctuation", "symbol" ]
        },
        "edge_ngram_tokenizer" : {
          "type" : "edge_ngram",
          "min_gram" : "1",
          "max_gram" : "3",
          "decompound_mode" : "mixed",
          "token_chars" : [
            "letter", "digit", "punctuation", "symbol"
          ]
        }
      },
      "filter" : {
        "edge_ngram_filter_front" : {
          "type" : "edge_ngram",
          "min_gram" : "1",
          "max_gram" : "3",
          "side" : "front"
        },
        "javacafe_chosung_filter" : {
          "type" : "javacafe_chosung"
        },
        "javacafe_jamo_filter" : {
          "type" : "javacafe_jamo"
        },
        "synonym_filter": {
          "type" : "synonym",
          "ignore_case" : true,
          "synonyms_path" : "analysis/synonym.txt"
        }
      }
    },
    "max_ngram_diff" : 50,
    "number_of_shards" : 6,
    "number_of_replicas" : 1
  }
}

 

 

스럽에서 사용하고 있는 분석기들이다.

윗 부분에서는 분석기를, 아래 부분에서는 토크나이저와 필터틑 정의하고 있다. 그리고 분석기에서 사용할 필터와 토크나이저를 추가해준다.  java_cafe와 같은 외부에서 만든 필터나 토크나이저를 사용하기도한다. 

 

synonym_filter는 동의어를 처리해주는 필터인데, synonyms_path를 통해 동의어 사전을 불러오고있다. 

 

4. 동의어 사전

동의어 원문에 특정 단어가 존재하지 않더라도 색인 데이터를 토큰화해서 저장할 때 동의어나 유의어에 해당하는 단어를 함께 저장해서 검색이 가능해지게 하는 기술이다. 예를 들어 "Elasticsearch" 라는 단어가 포함된 원문이 필터를 통해 인덱스에 저장된다면 "엘라스틱서치"라고 검색했을 때 검색되지 않는다. 하지만 동의어 기능을 이용하여 색인할 때 "엘라스틱서치"도 함께 저장한다면 "Elasticsearch"로도 검색이 가능하고 "엘라스틱서치"로도 색인이 가능하다. 

 

- 동의어 사전 만들기

동의어 파일은 엘라스틱서치가 설치된 서버 아래의 config 디렉토리 아래에 analysis라는 디렉터리를 만들고 synonym.txt.라는 파일을 생성한다. 

동의어를 추가할 때 단어를 쉼표로 분리해 등록한다. 예를 들어 "Elasticsearh"와 "엘라스틱서치"를 동의어로 지정하고 싶다면 동의어 사전 파일에 " Elasticsearch, 엘라스틱서치"라고 등록한다. 

 

적용하기

 

바지, jean, 진
뷰티, 화장품, 코스메틱, cosmetic