검색 템플릿

원본 보기

검색 템플릿

검색 템플릿은 서로 다른 변수를 넣어 실행할 수 있도록 저장해 둔 검색입니다.

Elasticsearch를 검색 백엔드로 사용한다면, 검색창에서 입력받은 사용자 입력을 검색 템플릿의 매개변수로 전달할 수 있습니다. 이렇게 하면 Elasticsearch의 쿼리 문법을 사용자에게 노출하지 않고도 검색을 실행할 수 있습니다.

Elasticsearch를 커스텀 애플리케이션에 사용한다면, 검색 템플릿을 통해 앱 코드를 수정하지 않고도 검색 내용을 변경할 수 있습니다.

검색 템플릿을 생성하거나 업데이트하려면 create stored script API를 사용하세요.

요청의 sourcesearch API의 요청 본문과 동일한 매개변수를 지원합니다.source는 오픈 소스 프로젝트인 mustache.java에서 제공하는 Mustache 변수도 허용합니다.

일반적으로 Mustache 변수는 이중 중괄호로 감쌉니다: {{my-var}}. 템플릿 검색을 실행하면 Elasticsearch가 이 변수들을 params의 값으로 치환합니다. mustache 문법에 대해 더 알아보려면 Mustache.js manual을 참고하세요. 검색 템플릿의 lang은 반드시 mustache여야 합니다.

다음 요청은 idmy-search-template인 검색 템플릿을 생성합니다.

				PUT _scripts/my-search-template
					{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "match": {
          "message": "{{query_string}}"
        }
      },
      "from": "{{from}}",
      "size": "{{size}}"
    }
  }
}
		

Elasticsearch는 검색 템플릿을 클러스터 상태에 Mustache 스크립트로 저장합니다. Elasticsearch는 검색 템플릿을 template 스크립트 컨텍스트에서 컴파일합니다. 스크립트를 제한하거나 비활성화하는 설정은 검색 템플릿에도 영향을 줍니다.

서로 다른 params로 템플릿을 테스트하려면 render search template API를 사용하세요.

				POST _render/template
					{
  "id": "my-search-template",
  "params": {
    "query_string": "hello world",
    "from": 20,
    "size": 10
  }
}
		

렌더링되면 템플릿은 검색 요청 본문을 출력합니다.

{
  "template_output": {
    "query": {
      "match": {
        "message": "hello world"
      }
    },
    "from": "20",
    "size": "10"
  }
}
		

이 API로 인라인 템플릿을 테스트할 수도 있습니다.

				POST _render/template
					{
    "source": {
      "query": {
        "match": {
          "message": "{{query_string}}"
        }
      },
      "from": "{{from}}",
      "size": "{{size}}"
    },
  "params": {
    "query_string": "hello world",
    "from": 20,
    "size": 10
  }
}
		

검색 템플릿으로 검색을 실행하려면 search template API를 사용하세요. 요청마다 서로 다른 params를 지정할 수 있습니다.

				GET my-index/_search/template
					{
  "id": "my-search-template",
  "params": {
    "query_string": "hello world",
    "from": 0,
    "size": 10
  }
}
		

응답은 search API의 응답과 동일한 속성을 사용합니다.

{
  "took": 36,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 0.5753642,
    "hits": [
      {
        "_index": "my-index",
        "_id": "1",
        "_score": 0.5753642,
        "_source": {
          "message": "hello world"
        }
      }
    ]
  }
}
		

여러 템플릿 검색을 한 번의 요청으로 실행하려면 multi search template API를 사용하세요. 이런 요청은 개별 검색을 여러 번 실행하는 것보다 오버헤드가 적고 속도가 빠른 경우가 많습니다.

				GET my-index/_msearch/template
					{ }
{ "id": "my-search-template", "params": { "query_string": "hello world", "from": 0, "size": 10 }}
{ }
{ "id": "my-other-search-template", "params": { "query_type": "match_all" }}
		

검색 템플릿을 조회하려면 get stored script API를 사용하세요.

				GET _scripts/my-search-template
		

모든 검색 템플릿과 그 밖의 저장된 스크립트 목록을 가져오려면 cluster state API를 사용하세요.

				GET _cluster/state/metadata?pretty&filter_path=metadata.stored_scripts
		

검색 템플릿을 삭제하려면 delete stored script API를 사용하세요.

				DELETE _scripts/my-search-template
		

변수의 기본값을 설정하려면 다음 문법을 사용하세요:

{{my-var}}{{^my-var}}default value{{/my-var}}
		

템플릿 검색의 params에 값이 지정되지 않으면 검색은 기본값을 대신 사용합니다. 예를 들어 다음 템플릿은 fromsize의 기본값을 설정합니다.

				POST _render/template
					{
  "source": {
    "query": {
      "match": {
        "message": "{{query_string}}"
      }
    },
    "from": "{{from}}{{^from}}0{{/from}}",
    "size": "{{size}}{{^size}}10{{/size}}"
  },
  "params": {
    "query_string": "hello world"
  }
}
		

문자열을 URL 인코딩하려면 {{#url}} 함수를 사용하세요.

				POST _render/template
					{
  "source": {
    "query": {
      "term": {
        "url.full": "{{#url}}{{host}}/{{page}}{{/url}}"
      }
    }
  },
  "params": {
    "host": "http://example.com",
    "page": "hello-world"
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output": {
    "query": {
      "term": {
        "url.full": "http%3A%2F%2Fexample.com%2Fhello-world"
      }
    }
  }
}
		

배열 값을 쉼표로 구분된 문자열로 이어붙이려면 {{#join}} 함수를 사용하세요. 예를 들어 다음 템플릿은 두 개의 이메일 주소를 이어붙입니다.

				POST _render/template
					{
  "source": {
    "query": {
      "match": {
        "user.group.emails": "{{#join}}emails{{/join}}"
      }
    }
  },
  "params": {
    "emails": [ "user1@example.com", "user_one@example.com" ]
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output": {
    "query": {
      "match": {
        "user.group.emails": "user1@example.com,user_one@example.com"
      }
    }
  }
}
		

사용자 지정 구분자를 지정할 수도 있습니다.

				POST _render/template
					{
  "source": {
    "query": {
      "range": {
        "user.effective.date": {
          "gte": "{{date.min}}",
          "lte": "{{date.max}}",
          "format": "{{#join delimiter='||'}}date.formats{{/join delimiter='||'}}"
	      }
      }
    }
  },
  "params": {
    "date": {
      "min": "2098",
      "max": "06/05/2099",
      "formats": ["dd/MM/yyyy", "yyyy"]
    }
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output": {
    "query": {
      "range": {
        "user.effective.date": {
          "gte": "2098",
          "lte": "06/05/2099",
          "format": "dd/MM/yyyy||yyyy"
        }
      }
    }
  }
}
		

변수 값을 JSON 표현으로 변환하려면 {{#toJson}} 함수를 사용하세요.

예를 들어 다음 템플릿은 {{#toJson}}을 사용해 배열을 전달합니다. 요청 본문이 유효한 JSON이 되도록 source는 문자열 형식으로 작성되어 있습니다.

				POST _render/template
					{
  "source": "{ \"query\": { \"terms\": { \"tags\": {{#toJson}}tags{{/toJson}} }}}",
  "params": {
    "tags": [
      "prod",
      "es01"
    ]
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output": {
    "query": {
      "terms": {
        "tags": [
          "prod",
          "es01"
        ]
      }
    }
  }
}
		

{{#toJson}}으로 객체를 전달할 수도 있습니다.

				POST _render/template
					{
  "source": "{ \"query\": {{#toJson}}my_query{{/toJson}} }",
  "params": {
    "my_query": {
      "match_all": { }
    }
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output" : {
    "query" : {
      "match_all" : { }
    }
  }
}
		

객체 배열을 전달할 수도 있습니다.

				POST _render/template
					{
  "source": "{ \"query\": { \"bool\": { \"must\": {{#toJson}}clauses{{/toJson}} }}}",
  "params": {
    "clauses": [
      {
        "term": {
          "user.id": "kimchy"
        }
      },
      {
        "term": {
          "url.domain": "example.com"
        }
      }
    ]
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output": {
    "query": {
      "bool": {
        "must": [
          {
            "term": {
              "user.id": "kimchy"
            }
          },
          {
            "term": {
              "url.domain": "example.com"
            }
          }
        ]
      }
    }
  }
}
		

if 조건을 만들려면 다음 문법을 사용하세요:

{{#condition}}content{{/condition}}
		

조건 변수가 true이면 Elasticsearch는 그 내용을 표시합니다. 예를 들어 다음 템플릿은 year_scopetrue일 때 지난 1년간의 데이터를 검색합니다.

				POST _render/template
					{
  "source": "{ \"query\": { \"bool\": { \"filter\": [ {{#year_scope}} { \"range\": { \"@timestamp\": { \"gte\": \"now-1y/d\", \"lt\": \"now/d\" } } }, {{/year_scope}} { \"term\": { \"user.id\": \"{{user_id}}\" }}]}}}",
  "params": {
    "year_scope": true,
    "user_id": "kimchy"
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output" : {
    "query" : {
      "bool" : {
        "filter" : [
          {
            "range" : {
              "@timestamp" : {
                "gte" : "now-1y/d",
                "lt" : "now/d"
              }
            }
          },
          {
            "term" : {
              "user.id" : "kimchy"
            }
          }
        ]
      }
    }
  }
}
		

year_scopefalse이면 템플릿은 기간 제한 없이 데이터를 검색합니다.

				POST _render/template
					{
  "source": "{ \"query\": { \"bool\": { \"filter\": [ {{#year_scope}} { \"range\": { \"@timestamp\": { \"gte\": \"now-1y/d\", \"lt\": \"now/d\" } } }, {{/year_scope}} { \"term\": { \"user.id\": \"{{user_id}}\" }}]}}}",
  "params": {
    "year_scope": false,
    "user_id": "kimchy"
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output" : {
    "query" : {
      "bool" : {
        "filter" : [
          {
            "term" : {
              "user.id" : "kimchy"
            }
          }
        ]
      }
    }
  }
}
		

if-else 조건을 만들려면 다음 문법을 사용하세요:

{{#condition}}if content{{/condition}} {{^condition}}else content{{/condition}}
		

예를 들어 다음 템플릿은 year_scopetrue이면 지난 1년간의 데이터를 검색합니다. 그렇지 않으면 지난 하루의 데이터를 검색합니다.

				POST _render/template
					{
  "source": "{ \"query\": { \"bool\": { \"filter\": [ { \"range\": { \"@timestamp\": { \"gte\": {{#year_scope}} \"now-1y/d\" {{/year_scope}} {{^year_scope}} \"now-1d/d\" {{/year_scope}} , \"lt\": \"now/d\" }}}, { \"term\": { \"user.id\": \"{{user_id}}\" }}]}}}",
  "params": {
    "year_scope": true,
    "user_id": "kimchy"
  }
}
		

mustache 템플릿 언어는 템플릿 안에서 사용할 수 있는 여러 태그 유형을 정의합니다. 다음 섹션들은 그중 일부 태그 유형을 설명하고 Elasticsearch 검색 템플릿에서 사용하는 예시를 제공합니다.

mustache 태그는 일반적으로 이중 중괄호로 감쌉니다. mustache 변수 {{my-variable}}는 mustache 태그의 한 유형입니다. 템플릿 검색을 실행하면 Elasticsearch가 이 변수들을 params의 값으로 치환합니다.

예를 들어 다음 검색 템플릿을 살펴보겠습니다:

				PUT _scripts/my-search-template
					{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "match": {
          "message": "{{query_string}}"
        }
      },
      "from": "{{from}}",
      "size": "{{size}}"
    }
  }
}
		

위 검색 템플릿을 params와 함께 테스트합니다:

				POST _render/template
					{
  "id": "my-search-template",
  "params": {
    "query_string": "hello world",
    "from": 20,
    "size": 10
  }
}
		

렌더링되면 message{{query_string}}params로 전달된 hello world로 치환됩니다.

{
  "template_output": {
    "query": {
      "match": {
        "message": "hello world"
      }
    },
    "from": "20",
    "size": "10"
  }
}
		

검색 템플릿이 query_string에 값을 전달하지 않으면 message는 빈 문자열로 치환됩니다.

예를 들면 다음과 같습니다:

				POST _render/template
					{
  "id": "my-search-template",
  "params": {
    "from": 20,
    "size": 10
  }
}
		

렌더링되면 템플릿은 다음과 같이 출력됩니다:

{
  "template_output": {
    "query": {
      "match": {
        "message": ""
      }
    },
    "from": "20",
    "size": "10"
  }
}
		

섹션도 Mustache 태그의 한 유형입니다. 검색 템플릿에서 중첩된 객체나 중첩되지 않은 객체와 함께 sections를 사용할 수 있습니다. 섹션은 {{#my-section-variable}}로 시작하고 {{/my-section-variable}}로 끝납니다.

다음 검색 템플릿은 중첩 객체와 함께 섹션을 사용하는 예시를 보여줍니다:

				POST _render/template
					{
  "source":
  """
  {
    "query": {
      "match": {
        {{#query_message}}
          {{#query_string}}
        "message": "Hello {{#first_name_section}}{{first_name}}{{/first_name_section}} {{#last_name_section}}{{last_name}}{{/last_name_section}}"
          {{/query_string}}
        {{/query_message}}
      }
    }
  }
  """,
  "params": {
    "query_message": {
       "query_string": {
         "first_name_section": {"first_name": "John"},
         "last_name_section": {"last_name": "kimchy"}
       }
    }
  }
}
		

템플릿은 다음과 같이 렌더링됩니다:

{
  "template_output": {
    "query": {
      "match": {
        "message": "Hello John kimchy"
      }
    }
  }
}
		

객체 목록을 전달하고 검색 템플릿에서 각 항목을 순회할 수 있습니다.

예를 들어 다음 검색 템플릿은 섹션을 조합해 모든 사용자 이름을 매칭합니다:

				PUT _scripts/my-search-template
					{
  "script": {
    "lang": "mustache",
    "source": """
    {
      "query":{
        "multi_match":{
          "query": "{{query_string}}",
          "fields": [{{#text_fields}}"{{field_name}}",{{/text_fields}}]
        }
      }
    }
    """
  }
}
		

템플릿 테스트:

				POST _render/template
					{
  "id": "my-search-template",
  "params": {
    "query_string": "My string",
    "text_fields": [
      {
        "field_name": "first_name"
      },
      {
        "field_name": "last_name"
      }
    ]
  }
}
		

렌더링되면 템플릿은 다음을 출력합니다:

{
  "template_output": {
    "query": {
      "multi_match": {
        "query": "My string",
        "fields": ["first_name","last_name",]
      }
    }
  }
}
		
참고

위의 경우 후행 쉼표 문제가 발생해 JSON이 유효하지 않게 됩니다. 이를 해결하려면 역섹션을 포함하고 배열의 마지막 항목인지 확인하는 변수를 추가하면 됩니다.

예를 들면 다음과 같습니다:

				PUT _scripts/my-search-template
					{
  "script": {
    "lang": "mustache",
    "source": """
    {
      "query":{
        "multi_match":{
          "query": "{{query_string}}",
          "fields": [{{#text_fields}}"{{field_name}}"{{^last}},{{/last}}{{/text_fields}}]
        }
      }
    }
    """
  }
}
		

배열의 마지막 항목인지 판별하는 변수 last와 함께 my-search-template을 다시 테스트합니다:

				POST _render/template
					{
  "id": "my-search-template",
  "params": {
    "query_string": "My string",
    "text_fields": [
      {
        "field_name": "first_name",
        "last": false
      },
      {
        "field_name": "last_name",
        "last": true
      }
    ]
  }
}
		

렌더링되면 템플릿은 다음을 출력합니다:

{
  "template_output": {
    "query": {
      "multi_match": {
        "query": "My string",
        "fields": [
          "first_name",
          "last_name"
        ]
      }
    }
  }
}
		

Elasticsearch는 텍스트를 특정 형식으로 변환할 수 있도록 미리 만들어진 커스텀 함수를 제공합니다.

mustache 람다 사용법에 대해 더 알아보려면 문자열 URL 인코딩, 값 이어붙이기, JSON으로 변환하기의 예시를 확인하세요.

역섹션은 값을 한 번만 설정하고자 할 때 유용합니다.

역섹션을 사용하려면 다음 문법을 사용하세요:

{{^my-variable}} content {{/my-variable}}
		

예를 들어 다음 검색 템플릿에서 name_existsfalse이면 messageHello World로 설정되고, 그렇지 않으면 빈 문자열로 설정됩니다.

				POST _render/template
					{
  "source": {
    "query": {
      "match": {
        "message": "{{^name_exists}}Hello World{{/name_exists}}"
      }
    }
  },
  "params": {
     "name_exists": false
  }
}
		

역섹션은 조건기본값과 함께 사용할 수도 있습니다.

예를 들어 다음 검색 템플릿에서 name_existstrue이면 {{query_string}}의 값이 치환됩니다. name_existsfalse이면 기본값 World로 설정됩니다.

				POST _render/template
					{
  "source": {
    "query": {
      "match": {
        "message": "Hello {{#name_exists}}{{query_string}}{{/name_exists}}{{^name_exists}}World{{/name_exists}}"
      }
    }
  },
  "params": {
    "query_string": "Kimchy",
     "name_exists": true
  }
}
		

렌더링되면 템플릿 출력은 다음과 같습니다:

{
  "template_output": {
    "query": {
      "match": {
        "message": "Hello Kimchy"
      }
    }
  }
}
		

검색 템플릿에서 기본 구분자인 이중 중괄호 {{my-variable}}를 원하는 사용자 지정 구분자로 변경할 수 있습니다.

예를 들어 다음 검색 템플릿은 기본 구분자를 홑괄호 (query_string)로 변경합니다.

				PUT _scripts/my-search-template
					{
  "script": {
    "lang": "mustache",
    "source":
    """
    {
      "query": {
        "match": {
           {{=( )=}}
          "message": "(query_string)"
          (={{ }}=)
        }
      }
    }
    """
  }
}
		

새 구분자로 템플릿 테스트:

				POST _render/template
					{
  "id": "my-search-template",
  "params": {
    "query_string": "hello world"
  }
}
		

렌더링되면 템플릿은 다음을 출력합니다:

{
  "template_output": {
    "query": {
      "match": {
        "message": "hello world"
      }
    }
  }
}
		

다음 mustache 기능은 Elasticsearch 검색 템플릿에서 지원되지 않습니다:

  • Partials