☆IT 개발 프로그램☆/Open Source

[ElasticSearch 엘라스틱서치] 멀티 필드 맵핑, 정렬과 검색을 동시에

호기심을 품고사는 중 2020. 6. 4. 15:13

엘라스틱서치, Mapping?

엘라스틱서치 안에서 맵핑이란, 사용될 필드들의 타입(자료형)을 사전에 정의해주는 것이다. 관계형 데이터베이스로 비유하자면 스키마 정의와 같다. 텍스트 타입 이외에도 다양한 숫자 타입, 날짜 타입, 위치 데이터 등으로 정의할 수 있으며 디테일한 커스텀 설정이 가능하다. 이 포스팅은 맵핑안에서 "fields" 파라미터를 이용하여, 한 필드가 다양한 특성을 동시에 가질 수 있도록 커스텀 맵핑하는 법에 대해 설명한다.

 


1. 맵핑 기본문법 SYNTAX

# MAPPING

PUT /my-index
{
  "mappings": {
    "properties": {
      "age":    { "type": "integer" },  
      "email":  { "type": "keyword"  }, 
      "name":   { "type": "text"  }     
    }
  }
}

my-index 라는 인덱스에 사용될 필드들을 맵핑하였다. age 필드는 integer 타입, email 필드는 keyword 타입, name 필드는 text를 가지도록 맵핑한다.

 


 

# UPDATE

PUT /my-index/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}

기존의 index에 새로운 필드 맵핑을 추가할수도 있다. employee-id 라는 keyword 타입의 필드을 새로 정의하였다.
단, 이미 정의된 필드의 경우, 타입 변경은 불가능하고 "fields" (이 포스팅에서 설명할 멀티 필드 기능) / "ignore_above" 파라미터를 수정하는 경우에 한해서만 허용된다. 



# VIEW

GET /my-index/_mapping

정의된 맵핑을 조회하는 쿼리이다. 예상되는 리턴은 아래와 같다.

{
  "my-index" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "integer"
        },
        "email" : {
          "type" : "keyword"
        },
        "employee-id" : {
          "type" : "keyword",
          "index" : false
        },
        "name" : {
          "type" : "text"
        }
      }
    }
  }
}

 


2. 필드 데이터 타입

 

TYPE 구분 타입
단순형 (SIMPLE TYPE) TEXT: 풀 텍스트 (상품 설명, 질문 답변)
KEYWORD: 인덱스화되어있는 키워드타입의 데이터 (예시: 이메일주소, 우편번호)
DATE: 날짜형, LONG: 숫자형, DOUBLE: 숫자형, BOOLEAN: 바이너리, IP
계급형 (HIERARCHICAL TYPE) OBJECT, NESTED
특수형 (SPECIALISED TYPE) GEO_POINT, GEO_SHAPE, COMPLETION

다양한 타입으로 정의할 수 있다 (참조)


3. 멀티 필드 맵핑

멀티 필드 맵핑의 활용법

- 특정 필드에 풀 텍스트 검색 & 정렬 등을 동시에 사용하고 싶을 때

- 특정 필드에 여러 가지 분석기(Analyzer)를 적용하고 싶을 때

 

멀티 필드 맵핑은, 엘라스틱 서치 안에서 동일한 필드를 다른 용도로 사용하고자 할 때에 무척 유용하다. 예를 들어 '상품 이름' 이라는 항목에 analyzer도 사용하고 싶고, 동시에 클러스터링이나 정렬도 사용하고 싶다면 난감할 수 있다. 키워드 필드 데이터에는 analyzer를 사용할 수 없고, text 형 데이터는 정렬기능이 지원되지 않기 때문이다. 두 가지 특성을 모두 사용할 수 없을까? 이 경우는 "fields" 파라미터를 추가해, 멀티 맵핑을 하면 된다.


멀티 필드 맵핑 쿼리

PUT my_index
{
  "mappings": {
    "properties": {
      "city": {
        "type": "text",
        "analyzer": "english"
        "fields": {
          "raw": { 
            "type":  "keyword"
            "normalizer": "custom_normalizer"
          }
        }
      }
    }
  }
}

city 라는 이름의 데이터 타입을 정의하였다. 기본 타입은 text 타입에, english 분석기를 사용하도록 했다. 그리고 fields 파라미터로 "raw" 라는 자식을 따로 정의하였다. 자식 필드는 keyword 타입에, custom_normalizer 라는 노멀라이저를 사용한다. (*분석기가 텍스트 타입에만 사용가능한 반면, 노멀라이저는 키워드 타입에 사용할 수 있고, 성능은 거의 동일한데 노멀라이즈는 단일화 토큰을 생성한다. 자세한 것은 링크 참조) 

 

 

맵핑 후 테스트 데이터 삽입

PUT my_index/_doc/1
{
  "city": "New York"
}

PUT my_index/_doc/2
{
  "city": "York"
}

city 필드에 "New York", "York"라는 데이터 2개를 삽입하였다. 

 

 

검색 쿼리

GET my_index/_search
{
  "query": {
    "match": {
      "city": "york" 
    }
  },
  "sort": {
    "city.raw": "asc" 
  },
  "aggs": {
    "Cities": {
      "terms": {
        "field": "city.raw" 
      }
    }
  }
}

city는 멀티 필드 맵핑에 의해서, 분석기가 적용된 text 타입과 노멀라이저가 적용된 keyword 타입 양 쪽 모두 가지므로 검색, 정렬, 클러스터링에 동시에 사용될 수 있다. 검색쿼리의 타겟은 "city" 필드가(text 타입) 타겟이 되고, 정렬과 클러스터링 쿼리는 "city.raw"(keyword 타입) 필드가 타겟이 된다. 


기타

 

- 하위 노드의 명칭은 편의에 따라 raw 이외의 이름으로 변경할수도 있다.