동적 템플릿

원본 보기

동적 템플릿

동적 템플릿을 사용하면 기본 동적 필드 매핑 규칙을 넘어 Elasticsearch가 데이터를 매핑하는 방식을 더 세밀하게 제어할 수 있습니다. dynamic 파라미터를 true 또는 runtime으로 설정하면 동적 매핑이 활성화됩니다. 그런 다음 동적 템플릿을 사용해, 일치 조건에 따라 동적으로 추가된 필드에 적용할 사용자 정의 매핑을 정의할 수 있습니다:

  • match_mapping_typeunmatch_mapping_type은 Elasticsearch가 감지한 데이터 타입을 기준으로 동작합니다
  • matchunmatch는 패턴을 사용해 필드 이름을 매칭합니다
  • path_matchpath_unmatch는 필드까지의 전체 점(dot) 경로를 기준으로 동작합니다
  • 동적 템플릿이 match_mapping_type, match, path_match를 정의하지 않으면 어떤 필드와도 일치하지 않습니다. 다만 bulk 요청dynamic_templates 섹션에서 템플릿을 이름으로 참조할 수는 있습니다.

매핑 정의에서 {{name}}{{dynamic_type}} 템플릿 변수를 플레이스홀더로 사용하세요.

중요

동적 필드 매핑은 필드에 실제 값이 있을 때만 추가됩니다. 필드가 null이거나 빈 배열이면 Elasticsearch는 동적 필드 매핑을 추가하지 않습니다. dynamic_template에서 null_value 옵션을 사용한 경우, 해당 필드에 실제 값이 있는 첫 문서가 색인된 이후에만 적용됩니다.

동적 템플릿은 이름이 지정된 객체의 배열로 정의합니다:

"dynamic_templates": [
  {
    "my_template_name": {
      ... match conditions ...
      "mapping": { ... }
    }
  },
  ...
]
		
  1. 템플릿 이름은 임의의 문자열 값이 될 수 있습니다.
  2. 일치 조건에는 match_mapping_type, match, match_pattern, unmatch, path_match, path_unmatch 중 어느 것이든 포함될 수 있습니다.
  3. 일치한 필드가 사용할 매핑입니다.

제공된 매핑에 유효하지 않은 매핑 스니펫이 포함되어 있으면 검증 오류가 반환됩니다. 검증은 색인 시점에 동적 템플릿이 적용될 때 수행되며, 대부분의 경우 동적 템플릿이 업데이트될 때도 수행됩니다. 유효하지 않은 매핑 스니펫을 제공하면 특정 조건에서 동적 템플릿의 업데이트나 검증이 실패할 수 있습니다:

  • match_mapping_type이 지정되지 않았지만 템플릿이 미리 정의된 매핑 타입 중 최소 하나에 대해 유효하다면, 해당 매핑 스니펫은 유효한 것으로 간주됩니다. 그러나 템플릿에 일치하는 필드가 다른 타입으로 색인되면 색인 시점에 검증 오류가 반환됩니다. 예를 들어 match_mapping_type 없이 구성한 동적 템플릿은 string 타입으로는 유효하다고 간주되지만, 그 동적 템플릿에 일치하는 필드가 long으로 색인되면 색인 시점에 검증 오류가 반환됩니다. match_mapping_type을 예상되는 JSON 타입으로 설정하거나 매핑 스니펫에 원하는 type을 설정하는 것을 권장합니다.
  • 매핑 스니펫에 {{name}} 플레이스홀더를 사용하면 동적 템플릿을 업데이트할 때 검증이 생략됩니다. 그 시점에는 필드 이름을 알 수 없기 때문입니다. 대신 색인 시점에 템플릿이 적용될 때 검증이 수행됩니다.

템플릿은 순서대로 처리되며, 가장 먼저 일치하는 템플릿이 적용됩니다. update mapping API로 새 동적 템플릿을 등록하면 기존 템플릿이 모두 덮어써집니다. 덕분에 처음 추가한 이후에도 동적 템플릿의 순서를 바꾸거나 삭제할 수 있습니다.

Elasticsearch가 특정 타입의 새 필드를 런타임 필드로 동적 매핑하도록 하려면 인덱스 매핑에 "dynamic":"runtime"을 설정하세요. 이 필드들은 색인되지 않으며 쿼리 시점에 _source에서 로드됩니다.

또는 기본 동적 매핑 규칙을 사용하면서 동적 템플릿을 만들어 특정 필드를 런타임 필드로 매핑할 수도 있습니다. 인덱스 매핑에 "dynamic":"true"를 설정한 다음, 특정 타입의 새 필드를 런타임 필드로 매핑하는 동적 템플릿을 만들면 됩니다.

각 필드가 ip_로 시작하는 데이터가 있다고 가정해 봅시다. 동적 매핑 규칙에 따라 Elasticsearch는 numeric 감지를 통과하는 stringfloatlong으로 매핑합니다. 하지만 새 문자열을 ip 타입의 런타임 필드로 매핑하는 동적 템플릿을 만들 수 있습니다.

다음 요청은 strings_as_ip라는 동적 템플릿을 정의합니다. Elasticsearch가 ip* 패턴에 일치하는 새 string 필드를 감지하면 해당 필드를 ip 타입의 런타임 필드로 매핑합니다. ip 필드는 동적으로 매핑되지 않기 때문에, 이 템플릿은 "dynamic":"true""dynamic":"runtime" 어느 쪽과도 함께 사용할 수 있습니다.

				PUT my-index-000001/
					{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_ip": {
          "match_mapping_type": "string",
          "match": "ip*",
          "runtime": {
            "type": "ip"
          }
        }
      }
    ]
  }
}
		

동적 템플릿으로 string 필드를 색인 필드 또는 런타임 필드로 매핑하는 방법은 이 예제를 참고하세요.

match_mapping_type 파라미터는 JSON 파서가 감지한 데이터 타입으로 필드를 매칭하고, unmatch_mapping_type은 데이터 타입을 기준으로 필드를 제외합니다.

JSON은 longinteger, doublefloat를 구분하지 않기 때문에, 파싱된 부동소수점 숫자는 모두 double JSON 데이터 타입으로 간주되고 파싱된 integer 숫자는 모두 long으로 간주됩니다.

참고

동적 매핑에서 Elasticsearch는 항상 더 넓은 데이터 타입을 선택합니다. 유일한 예외는 float로, double보다 저장 공간을 적게 쓰면서 대부분의 애플리케이션에 충분히 정밀합니다. 런타임 필드는 float를 지원하지 않기 때문에 "dynamic":"runtime"double을 사용합니다.

Elasticsearch는 다음 데이터 타입을 자동으로 감지합니다:

Elasticsearch 데이터 타입
JSON 데이터 타입 "dynamic":"true" "dynamic":"runtime"
null 필드가 추가되지 않음 필드가 추가되지 않음
true 또는 false boolean boolean
double float double
long long long
object object 필드가 추가되지 않음
array 배열에서 null이 아닌 첫 번째 값에 따라 결정 배열에서 null이 아닌 첫 번째 값에 따라 결정
날짜 감지를 통과하는 string date date
숫자 감지를 통과하는 string float 또는 long double 또는 long
date 감지와 numeric 감지를 모두 통과하지 못하는 string .keyword 하위 필드를 가진 text keyword

match_mapping_type이나 unmatch_mapping_type 파라미터에는 단일 데이터 타입 또는 데이터 타입 목록을 지정할 수 있습니다. 또한 match_mapping_type 파라미터에는 와일드카드(*)를 사용해 모든 데이터 타입에 일치시킬 수 있습니다.

예를 들어 모든 정수 필드를 long 대신 integer로 매핑하고 모든 string 필드를 textkeyword 양쪽으로 매핑하려면 다음 템플릿을 사용할 수 있습니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "numeric_counts": {
          "match_mapping_type": ["long", "double"],
          "match": "count",
          "mapping": {
            "type": "{dynamic_type}",
            "index": false
          }
        }
      },
      {
        "integers": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "integer"
          }
        }
      },
      {
        "strings": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "fields": {
              "raw": {
                "type":  "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      },
      {
        "non_objects_keyword": {
          "match_mapping_type": "*",
          "unmatch_mapping_type": "object",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}
				PUT my-index-000001/_doc/1
					{
  "my_integer": 5,
  "my_string": "Some string",
  "my_boolean": "false",
  "field": {"count": 4}
}
		
  1. my_integer 필드는 integer로 매핑됩니다.
  2. my_string 필드는 keyword 멀티 필드를 가진 text로 매핑됩니다.
  3. my_boolean 필드는 keyword로 매핑됩니다.
  4. field.count 필드는 long으로 매핑됩니다.

match 파라미터는 하나 이상의 패턴으로 필드 이름을 매칭하고, unmatch는 하나 이상의 패턴으로 match에 일치한 필드를 제외합니다.

match_pattern 파라미터는 match 파라미터의 동작을 조정해, 단순 와일드카드 대신 필드 이름에 대한 완전한 Java 정규 표현식 매칭을 지원하게 합니다. 예를 들면:

"match_pattern": "regex",
"match": "^profit_\d+$"
		

다음 예제는 이름이 long_로 시작하는 모든 string 필드(단, _text로 끝나는 필드는 제외)에 일치시켜 long 필드로 매핑합니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "longs_as_strings": {
          "match_mapping_type": "string",
          "match":   "long_*",
          "unmatch": "*_text",
          "mapping": {
            "type": "long"
          }
        }
      }
    ]
  }
}
				PUT my-index-000001/_doc/1
					{
  "long_num": "5",
  "long_text": "foo"
}
		
  1. long_num 필드는 long으로 매핑됩니다.
  2. long_text 필드는 기본 string 매핑을 사용합니다.

matchunmatch 필드에는 JSON 배열을 사용해 패턴 목록을 지정할 수 있습니다.

다음 예제는 이름이 ip_로 시작하거나 _ip로 끝나는 모든 필드에 일치시키되, one으로 시작하거나 two로 끝나는 필드는 제외하고 ip 필드로 매핑합니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "ip_fields": {
          "match":   ["ip_*", "*_ip"],
          "unmatch": ["one*", "*two"],
          "mapping": {
            "type": "ip"
          }
        }
      }
    ]
  }
}
				PUT my-index-000001/_doc/1
					{
  "one_ip":   "will not match",
  "ip_two":   "will not match",
  "three_ip": "12.12.12.12",
  "ip_four":  "13.13.13.13"
}
		
  1. one_ip 필드는 제외되므로 기본 매핑인 text를 사용합니다.
  2. ip_two 필드는 제외되므로 기본 매핑인 text를 사용합니다.
  3. three_ip 필드는 ip 타입으로 매핑됩니다.
  4. ip_four 필드는 ip 타입으로 매핑됩니다.

path_matchpath_unmatch 파라미터는 matchunmatch와 같은 방식으로 동작하지만, 마지막 이름만이 아니라 필드까지의 전체 점(dot) 경로를 기준으로 동작합니다. 예: some_object.*.some_field.

다음 예제는 name 객체에 있는 모든 필드의 값을 최상위 full_name 필드로 복사하되, middle 필드는 제외합니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "full_name": {
          "path_match":   "name.*",
          "path_unmatch": "*.middle",
          "mapping": {
            "type":       "text",
            "copy_to":    "full_name"
          }
        }
      }
    ]
  }
}
				PUT my-index-000001/_doc/1
					{
  "name": {
    "first":  "John",
    "middle": "Winston",
    "last":   "Lennon"
  }
}
		

다음 예제는 path_matchpath_unmatch 양쪽에 패턴 배열을 사용합니다.

name 객체 또는 user.name 객체에 있는 모든 필드의 값이 최상위 full_name 필드로 복사되며, middlemidinitial 필드는 제외됩니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "full_name": {
          "path_match":   ["name.*", "user.name.*"],
          "path_unmatch": ["*.middle", "*.midinitial"],
          "mapping": {
            "type":       "text",
            "copy_to":    "full_name"
          }
        }
      }
    ]
  }
}
				PUT my-index-000001/_doc/1
					{
  "name": {
    "first":  "John",
    "middle": "Winston",
    "last":   "Lennon"
  }
}
				PUT my-index-000001/_doc/2
					{
  "user": {
    "name": {
      "first":      "Jane",
      "midinitial": "M",
      "last":       "Salazar"
    }
  }
}
		

path_matchpath_unmatch 파라미터는 리프 필드뿐 아니라 객체 경로에도 일치합니다. 예를 들어 다음 문서를 색인하면 오류가 발생하는데, path_match 설정이 객체 필드 name.title에도 일치하고 이 필드는 text로 매핑될 수 없기 때문입니다:

				PUT my-index-000001/_doc/2
					{
  "name": {
    "first":  "Paul",
    "last":   "McCartney",
    "title": {
      "value": "Sir",
      "category": "order of chivalry"
    }
  }
}
		

{{name}}{{dynamic_type}} 플레이스홀더는 mapping에서 필드 이름과 감지된 동적 타입으로 치환됩니다. 다음 예제는 모든 문자열 필드가 필드와 같은 이름의 analyzer를 사용하도록 설정하고, 문자열이 아닌 모든 필드에 대해 doc_values를 비활성화합니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "named_analyzers": {
          "match_mapping_type": "string",
          "match": "*",
          "mapping": {
            "type": "text",
            "analyzer": "{name}"
          }
        }
      },
      {
        "no_doc_values": {
          "match_mapping_type":"*",
          "mapping": {
            "type": "{dynamic_type}",
            "doc_values": false
          }
        }
      }
    ]
  }
}
				PUT my-index-000001/_doc/1
					{
  "english": "Some English text",
  "count":   5
}
		
  1. english 필드는 english 애널라이저를 사용하는 string 필드로 매핑됩니다.
  2. count 필드는 doc_values가 비활성화된 long 필드로 매핑됩니다.

유용하게 쓸 수 있는 동적 템플릿 예제를 몇 가지 소개합니다:

"dynamic":"true"를 설정하면 Elasticsearch는 문자열 필드를 keyword 하위 필드를 가진 text 필드로 매핑합니다. 구조화된 콘텐츠만 색인하고 전문 검색이 필요하지 않다면, Elasticsearch가 해당 필드를 keyword 필드로만 매핑하도록 할 수 있습니다. 다만 이 필드를 검색하려면 색인된 값과 정확히 동일한 값으로 검색해야 합니다.

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}
		

앞의 예제와 반대로, 문자열 필드에 대한 전문 검색만 필요하고 집계, 정렬, 정확 일치 검색을 수행할 계획이 없다면 Elasticsearch가 문자열을 text로 매핑하도록 지시할 수 있습니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_text": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text"
          }
        }
      }
    ]
  }
}
		

또는 매핑의 runtime 섹션에서 문자열 필드를 keyword 필드로 매핑하는 동적 템플릿을 만들 수도 있습니다. Elasticsearch가 string 타입의 새 필드를 감지하면 해당 필드는 keyword 타입의 런타임 필드로 생성됩니다.

string 필드가 색인되지는 않지만, 값은 _source에 저장되어 검색 요청, 집계, 필터링, 정렬에 사용할 수 있습니다.

예를 들어 다음 요청은 string 필드를 keyword 타입의 런타임 필드로 매핑하는 동적 템플릿을 생성합니다. runtime 정의가 비어 있더라도, Elasticsearch가 매핑에 필드 타입을 추가할 때 사용하는 동적 매핑 규칙에 따라 새 string 필드는 keyword 런타임 필드로 매핑됩니다. 날짜 감지나 숫자 감지를 통과하지 못하는 string은 자동으로 keyword로 매핑됩니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "runtime": {}
        }
      }
    ]
  }
}
		

간단한 문서를 색인합니다:

				PUT my-index-000001/_doc/1
					{
  "english": "Some English text",
  "count":   5
}
		

매핑을 확인해 보면 english 필드가 keyword 타입의 런타임 필드임을 알 수 있습니다:

				GET my-index-000001/_mapping
		
{
  "my-index-000001" : {
    "mappings" : {
      "dynamic_templates" : [
        {
          "strings_as_keywords" : {
            "match_mapping_type" : "string",
            "runtime" : { }
          }
        }
      ],
      "runtime" : {
        "english" : {
          "type" : "keyword"
        }
      },
      "properties" : {
        "count" : {
          "type" : "long"
        }
      }
    }
  }
}
		

norms는 색인 시점의 스코어링 요소입니다. 예를 들어 문서를 스코어로 정렬하지 않는 경우처럼 스코어링이 필요하지 않다면, 인덱스에 이 스코어링 요소를 저장하지 않도록 비활성화해 공간을 절약할 수 있습니다.

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "norms": false,
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    ]
  }
}
		

이 템플릿에 하위 keyword 필드가 포함된 것은 동적 매핑의 기본 규칙과 일관성을 유지하기 위해서입니다. 물론 이 필드에 대해 정확 일치 검색이나 집계를 수행할 필요가 없어 하위 필드가 필요 없다면, 앞 절에서 설명한 것처럼 제거할 수 있습니다.

Elasticsearch로 시계열 분석을 할 때는 자주 집계하지만 필터링은 전혀 하지 않는 숫자 필드가 많은 경우가 흔합니다. 이런 경우 해당 필드의 색인을 비활성화하면 디스크 공간을 절약하고 색인 속도도 어느 정도 높일 수 있습니다:

				PUT my-index-000001
					{
  "mappings": {
    "dynamic_templates": [
      {
        "unindexed_longs": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "long",
            "index": false
          }
        }
      },
      {
        "unindexed_doubles": {
          "match_mapping_type": "double",
          "mapping": {
            "type": "float",
            "index": false
          }
        }
      }
    ]
  }
}
		
  1. 기본 동적 매핑 규칙과 마찬가지로 double은 float로 매핑되며, 보통 충분히 정확하면서도 디스크 공간은 절반만 사용합니다.