본문 바로가기
엘라스틱서치

7. 인덱스 설정과 매핑

by N.Damgom 2021. 9. 6.

7. 인덱스 설정과 매핑

  • 데이터의 저장 및 검색 방법에 대한 설정이나 사용자 정의 애널라이저는 인덱스 단위로 저장된다.

7.1 설정 - Settings

  • 인덱스는 두 개의 정보 단위, settings 와 mappings로 이루어진다.
  • settings 또는 mappings 를 보는 보는 방법은 각각 _settings API 나 _mappings API를 사용한다.
  • 대부분의 설정은 한 번 지정하면 변경할 수 없다.
  • 샤드와 복제본 수 설정
    • number_of_shards와 number_of_replicas 라는 이름으로 "settings" - "index" 아래에 설정한다.
    • PUT my_index
      {
       "settings": {
         "index": {
           "number_of_shards": 3,
           "number_of_replicas": 1
        }
      }
      }
    • shrink API 또는 split API를 이용해서 샤드 수를 변경할 수 있으나, 복잡하므로 변경할 수 없다고 생각하자.
    • 복제본 수는 변경 가능하다.
  • refresh_interval: 세그먼트가 만들어지는 리프레시 타임을 설정한다. 기본은 1초이다.
    • PUT my_index
      {
       "settings": {
         "refresh_interval": "30s"
      }
      }
  • analyzer, tokenizer, filter
    • 애널라이저, 토크나이저 필터 등은 "settings"-"analysis" 하위에 지정한다.
    • PUT my_index
      {
       "settings": {
         "analysis": {
           "analyzer": { ## 애널라이저 지정
             "my_analyzer": {
               "type": "custom",
               "char_flter": [ "...", "..." ... ]
               "tokenizer": "...",
               "filter": [ "...", "..." ... ]
            }
          },
           "char_filter":{ ## 캐릭터 필터 지정
             "my_char_filter":{
               "type": "…"
               ... 
            }
          }
           "tokenizer": { ## 토크나이저
             "my_tokenizer":{
               "type": "…"
               ...
            }
          },
           "filter": { ## 토큰 필터
             "my_token_filter": {
               "type": "…"
               ...
            }
          }
        }
      }
      }
  • analysis는 변경 불가능하다. 변경이 필요할 시 재색인 과정을 거치자.

7.2 매핑 - Mappings

  • 엘라스틱서치는 동적 매핑을 지원하기 때문에 도큐먼트 추가 시 자동으로 매핑이 생성된다.
  • 매핑 정보는 "mappings" - "properties" 아래에 지정한다.
  • PUT books
    {
     "mappings": {
       "properties": {
         "category": {
           "type": "keyword"
        },
         "pages": {
           "type": "byte"
        },
         "title": {
           "type": "text"
        }
      }
    }
    }
  • 이미 만들어진 필드를 삭제하거나 타입 및 설정 값을 변경할 수는 없다.

7.2.1 문자열 - text, keyword

  • 매핑에 필드를 정의하지 않으면 동적 문자열 필드가 생성될 때 text, keyword 필드가 다중 필드로 같이 생성된다.
  • text: 풀 텍스트 검색에 사용될 문자열. 역 색인 구조를 생성한다.
    • "analyzer": "<애널라이저명>" - 색인에 사용할 애널라이저를 지정한다.
    • "search_analyzer":"<애널라이저명>" - 검색 시 색인 할때와 다른 애널라이저를 사용할 수 있다. Ngram 방식 사용시 지정이 바람직하다.
    • "index" : false로 지정시 해당 필드는 역색인 구조를 만들 지 않는다.(default: true)
    • "boost": 해당 필드 스코어 점수에 가중치를 부여한다. 1보다 낮은 값이면 가중치가 내려간다.(default: 1)
    • 인덱스 타임 boost는 5.0부터 deprecated. 쿼리 시에 사용하자.
    • "fielddata": true로 사용하면 집계 및 정렬이 가능하나 메모리를 많이 사용한다.(default: false)
  • keyword: 입력된 문자열을 하나의 토큰으로 저장한다. 보통 집계, 정렬에 사용한다.
    • index, boost: text와 동일
    • "doc_values": false 시 집계, 정렬을 위한 열기반 저장소를 생성하지 않아 집계 및 정렬이 불가능하다.(default: true)
    • "ignore_above": <자연수> - 해당 길이 이상의 문자열은 색인하지 않아 검색과 집계가 불가능하다.(default = Integer.MAX_VALUE, 동적 매핑시 256)
    • "nomalizer": "<노멀라이저명>" - 노멀라이저를 지정한다. 노멀라이저는 애널라이저-토크나이저이다.
  • PUT blogs
    {
     "settings": {
       "analysis": { ## analysis 아래에는 analyzer, filter, char_filter, nomalizer 가 있다.
         "analyzer": {
           "engram_a": {
             "tokenizer": "standard",
             "filter": [ "lowercase", "engram_f" ]
          }
        },
         "filter": {
           "engram_f": {
             "type": "edge_ngram",
             "min_gram": 2,
             "max_gram": 5
          }
        },
         "normalizer": {
           "norm_low": {
             "type": "custom",
             "filter": [ "lowercase", "asciifolding" ]
          }
        }
      }
    },
     "mappings": {
       "properties": {
         "title": {
           "type": "text",
           "boost": 2, ## deprecated
           "fields": {
             "keyword": {
               "type": "keyword",
               "normalizer": "norm_low"
            }
          }
        },
         "author": {
           "type": "text",
           "analyzer": "engram_a",
           "search_analyzer": "standard",
           "fields": {
             "keyword": {
               "type": "keyword",
               "ignore_above": 256
            }
          }
        },
         "synopsis": {
           "type": "text",
           "fielddata": true
        },
         "category": {
           "type": "keyword"
        },
         "content": {
           "type": "text",
           "index": false
        }
      }
    }
    }

7.2.2 숫자

  • long, integer, short, byte, double, float 등 기본 타입을 지원한다.
  • half_float: 16비트 실수
  • scaled_float: long 형태로 저장하고 소수점 위치를 지정한다.
  • "index", "doc_value", "boost" 는 문자와 동일
  • "coerce": <true|false> - 자동 타입 변환 (integer에 4.5 -> 4로 색인, 저장은 4.5, default는 true)
  • "null_value": <숫자값> - null 일시 default 값 지정
  • "ignore_malformed": <true|false> : true 시 숫자가 아닌 값이 들어와도 저장, 검색 및 집계에는 무시 (default는 false)
  • "scaled_float" 사용시
    • "scaling_factor": <10의 배수> - 예를 12.3456이란 값이 factor가 10일 경우 12.3으로 저장된다.

7.2.3 날짜

  • ISO8601 형식을 따라 입력한다.
    • "2019-06-12"
    • "2019-06-12T17:13:40+09:00"
    • "2019-06-12T17:13:40+09:00"
    • "2019-06-12T17:13:40.428Z"
  • epoch_mills, epoch_seconds, basic_date, strict_date_time도 사용가능
  • 이 외에는 text, keyword로 저장된다.
  • "doc_value", "index", "null_value", "ignore_malfomed"는 동일하게 동작
  • "format": "<문자열||문자열||..>"로 매핑 시 포맷팅
  • PUT my_date
    {
     "mappings": {
       "properties": {
         "date_val": {
           "type": "date",
           "format": "yyyy-MM-dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
        }
      }
    }
    }

    GET my_date/_search
    {
     "query": {
       "range": {
         "date_val": {
           "gt": "2019/09/10",
           "lt": 1568332800000
        }
      }
    }
    }
  • 입력과 검색 포맷이 달라도 잘 동작한다.

7.2.4 불리언

  • "true"라고 입력해도 true로 해석된다. 일반적으로 term 쿼리를 이용해서 검색한다.
  • "doc_value", "index" : 동일하게 동작한다.
  • "null_value": <true|false> - 지정하지 않으면 불리언필드가 없거나 null인 경우 존재하지 않는 것으로 처리되어 해당 도큐먼트는 쿼리나 집계에 나타나지 않는다.

7.2.5 Object와 Nested

  • Object: 복합 값 필드
    • PUT movie
      {
       "mappings": {
         "properties": {
           "characters": { ## 필드 이름
             "properties": { ## 필드 이름 하위에 다시 properties가 들어간다.
               "name": {
                 "type": "text"
              },
               "age": {
                 "type": "byte"
              },
               "side": {
                 "type": "keyword"
              }
            }
          }
        }
      }
      }

      GET movie/_search
      {
       "query": {
         "match": {
           "characters.name": "Iron Man" ## 마침표를 이용해서 접근한다.
        }
      }
      }
    • PUT movie/_doc/2
      {
       "title": "The Avengers",
       "characters": [ ## 여러 명의 캐릭터를 배열로 넣는다.
        {
           "name": "Iron Man",
           "side": "superhero"
        },
        {
           "name": "Loki",
           "side": "villain"
        }
      ]
      }

      PUT movie/_doc/3
      {
       "title": "Avengers: Infinity War",
       "characters": [
        {
           "name": "Loki",
           "side": "superhero"
        },
        {
           "name": "Thanos",
           "side": "villain"
        }
      ]
      }
    • 엘라스틱서치는 별도로 배열 필드를 저장하지 않고 그냥 배열을 입력값으로 넣어도 된다.
    • 역 색인에서는 object의 하위 필드는 모두 상위 이름과 함께 펼쳐져서 한 필드로 저장된다.
    • 따라서 이 경우 id:2 에는 character.name에는 [아이언맨,로키]가, character.side에는 [superhero, villan]이 포함되어 있는 것으로 역 색인 구조를 만든다.
    • 따라서 다음과 같이 쿼리하는 경우
    • GET movie/_search
      {
       "query": {
         "bool": {
           "must": [
            {
               "match": {
                 "characters.name": "Loki"
              }
            },
            {
               "match": {
                 "characters.side": "villain"
              }
            }
          ]
        }
      }
      }
    • id 2,3 문서가 동시에 검색된다.(둘다 로키와 빌런이 있으므로)
  • Nested: object 타입 필드에 있는 여러 object 값들이 서로 다른 역색인 구조를 사용해야할때 nested 타입이 필요하다.
    • PUT movie
      {
       "mappings": {
         "properties": {
           "characters": {
             "type": "nested", ## nested 타입으로 명시
             "properties": { ## object와 마찬가지로 필드명 아래 "properties"가 있다.
               "name": {
                 "type": "text"
              },
               "side": {
                 "type": "keyword"
              }
            }
          }
        }
      }
      }
    • 검색할 때도 nested 명시가 필요하다.
    • GET movie/_search
      {
       "query": {
         "nested": { ## nested 쿼리
           "path": "characters", ## nested로 정의된 필드 명시
           "query": {
             "bool": {
               "must": [
                {
                   "match": {
                     "characters.name": "Loki"
                  }
                },
                {
                   "match": {
                     "characters.side": "villain"
                  }
                }
              ]
            }
          }
        }
      }
      }
    • nested 필드 값은 내부적으로 별도의 도큐먼트로 분리되어 저장되며 쿼리 결과에서 상위 도큐먼트와 합쳐져서 보여지게 된다.

7.2.6 위치정보 - Geo

  • Geo Point
    • 위도(latitude), 경도(longitude) 두 실수를 가지고 지도 위의 한 점을 나타낸다.
    • PUT my_locations/_doc/1
      {
       "location": {
         "lat": 41.12,
         "lon": -71.34
      }
      }
    • geohash를 사용할 수도 있다.
    • PUT my_index/_doc/3
      {
       "location": "drm3btev3e86"
      }
    • 매핑은 "geo_point"로 선언한다.
    • PUT my_geo
      {
       "mappings": {
         "properties": {
           "location": {
             "type": "geo_point"
          }
        }
      }
      }
    • Geo Point는 반드시 사전에 인덱스 매핑이 필요하다.
  • geo_bounding_box 쿼리
    • 왼쪽 위와 오른쪽 아래의 좌표 값을 설정해서 네모 형태로 검색한다.
    • GET my_geo/_search
      {
       "query": {
         "geo_bounding_box": {
           "location": {
             "bottom_right": {
               "lat": 37.4899,
               "lon": 127.0388
            },
             "top_left": {
               "lat": 37.5779,
               "lon": 126.9617
            }
          }
        }
      }
      }
  • geo_distance 쿼리
    • distance를 반지름으로 하는 원형으로 검색한다.
    • GET my_geo/_search
      {
       "query": {
         "geo_distance": {
           "distance": "5km",
           "location": {
             "lat": 37.5358,
             "lon": 126.9559
          }
        }
      }
      }
  • Geo_Shape
    • 선 면등의 2차원 값을 지정하고 쿼리할 수 있다.
    • PUT my_shape
      {
       "mappings": {
         "properties": {
           "location": {
             "type": "geo_shape" # 필드 매핑시 geo_shape
          }
        }
      }
      }
    • 입력 데이터 type에는 "point ,"multipoint", "linestring","multilinestring","polygon","multipolygon","envelope"가 있다.
  • geo_shape 쿼리
    • geo shpae 타입의 값을 검색할려면 geo_shape 쿼리를 사용해야 한다.
    • GET my_shape/_search
      {
       "query": {
         "geo_shape": {
           "location": {
             "shape": {
               "type": "envelope",
               "coordinates": [
                [ 126.9687, 37.58 ],
                [ 126.99, 37.5543 ]
              ]
            },
             "relation": "intersects"
          }
        }
      }
      }
    • intersects: 위의 영역에 조금이라도 걸쳐있는 곳이 검색
    • within: 쿼리 영역에 완전히 포함되어 있어야 한다.
    • disjoint: 쿼리 영역 바깥에 있어야 한다.

7.2.7 기타 필드 타입 - IP, Range, Binary

  • IP : ip 형식을 저장한다.
  • range: 숫자나 날짜, IP 등을 시작과 끝이 있는 2차원 범위 형태로 저장한다.
    • PUT my_range
      {
       "mappings": {
         "properties": {
           "amount": {
             "type": "integer_range"
          },
           "days": {
             "type": "date_range"
          }
        }
      }
      }

      PUT my_range/_doc/1
      {
       "amount": {
         "gte": 19,
         "lt": 28
      },
       "days": {
         "gt": "2019-06-01T09:00:00",
         "lt": "2019-06-20"
      }
      }

      GET my_range/_search
      {
       "query": {
         "range": {
           "amount": {
             "gte": "16",
             "lte": "25",
             "relation": "intersects"
          }
        }
      }
      }
    • 검색 시 relation 옵션은 필수이다.
      • within: 도큐먼트 범위가 검색 범위 안에 완전히 포함되는 경우(검색이 넓다.)
      • cotains: 검색 범위가 도큐먼트 범위 안에 포함되는 경우(도큐먼트가 넓다.)
      • intersects: 공통 부분이 있는 경우
  • Binary: 바이너리 값을 저장할 수 있으나 색인되지 않는다.

7.3 멀티 필드 - Multi Field

  • 하나의 필드 값을 여러 개의 역 색인 및 docs_values 들로 저장할 수 있는 멀티 필드 기능이 있다.
  • PUT my_index
    {
     "mappings": {
       "properties": {
         "<필드명1>": {
           "type": "text",
           "fields": { # 필드 아래에 새로 필드를 정의힌다.
             "<필드명2>": { 
               "type": "<타입>"
            }
          }
        }
      }
    }
    }
  • PUT my_index
    {
     "settings": {
       "analysis": {
         "analyzer": {
           "nori_analyzer": {
             "tokenizer": "nori_tokenizer"
          }
        }
      }
    },
     "mappings": {
       "properties": {
         "message": {
           "type": "text",
           "fields": {
             "english": {
               "type": "text",
               "analyzer": "english" ## 다른 애널라이저를 사용한다.
            },
             "nori": {
               "type": "text",
               "analyzer": "nori_analyzer" ## nori 애널라이저 사용
            }
          }
        }
      }
    }
    }
  • 다국어 도큐먼트를 분석할 때 사용된다.