[[search-template]] == Search Template The `/_search/template` endpoint allows to use the mustache language to pre render search requests, before they are executed and fill existing templates with template parameters. [source,js] ------------------------------------------ GET /_search/template { "inline" : { "query": { "match" : { "{{my_field}}" : "{{my_value}}" } }, "size" : "{{my_size}}" }, "params" : { "my_field" : "foo", "my_value" : "bar", "my_size" : 5 } } ------------------------------------------ For more information on how Mustache templating and what kind of templating you can do with it check out the http://mustache.github.io/mustache.5.html[online documentation of the mustache project]. NOTE: The mustache language is implemented in elasticsearch as a sandboxed scripting language, hence it obeys settings that may be used to enable or disable scripts per language, source and operation as described in <> [float] ==== More template examples [float] ===== Filling in a query string with a single value [source,js] ------------------------------------------ GET /_search/template { "inline": { "query": { "match": { "title": "{{query_string}}" } } }, "params": { "query_string": "search for these words" } } ------------------------------------------ [float] ===== Passing an array of strings [source,js] ------------------------------------------ GET /_search/template { "inline": { "query": { "terms": { "status": [ "{{#status}}", "{{.}}", "{{/status}}" ] } } }, "params": { "status": [ "pending", "published" ] } } ------------------------------------------ which is rendered as: [source,js] ------------------------------------------ { "query": { "terms": { "status": [ "pending", "published" ] } } ------------------------------------------ [float] ===== Concatenating array of values The `{{#join}}array{{/join}}` function can be used to concatenate the values of an array as a comma delimited string: [source,js] ------------------------------------------ GET /_search/template { "inline": { "query": { "match": { "emails": "{{#join}}emails{{/join}}" } } }, "params": { "emails": [ "username@email.com", "lastname@email.com" ] } } ------------------------------------------ which is rendered as: [source,js] ------------------------------------------ { "query" : { "match" : { "emails" : "username@email.com,lastname@email.com" } } } ------------------------------------------ The function also accepts a custom delimiter: [source,js] ------------------------------------------ GET /_search/template { "inline": { "query": { "range": { "born": { "gte" : "{{date.min}}", "lte" : "{{date.max}}", "format": "{{#join delimiter='||'}}date.formats{{/join delimiter='||'}}" } } } }, "params": { "date": { "min": "2016", "max": "31/12/2017", "formats": ["dd/MM/yyyy", "yyyy"] } } } ------------------------------------------ which is rendered as: [source,js] ------------------------------------------ { "query" : { "range" : { "born" : { "gte" : "2016", "lte" : "31/12/2017", "format" : "dd/MM/yyyy||yyyy" } } } } ------------------------------------------ [float] ===== Default values A default value is written as `{{var}}{{^var}}default{{/var}}` for instance: [source,js] ------------------------------------------ { "inline": { "query": { "range": { "line_no": { "gte": "{{start}}", "lte": "{{end}}{{^end}}20{{/end}}" } } } }, "params": { ... } } ------------------------------------------ When `params` is `{ "start": 10, "end": 15 }` this query would be rendered as: [source,js] ------------------------------------------ { "range": { "line_no": { "gte": "10", "lte": "15" } } } ------------------------------------------ But when `params` is `{ "start": 10 }` this query would use the default value for `end`: [source,js] ------------------------------------------ { "range": { "line_no": { "gte": "10", "lte": "20" } } } ------------------------------------------ [float] ===== Converting parameters to JSON The `{{toJson}}parameter{{/toJson}}` function can be used to convert parameters like maps and array to their JSON representation: [source,js] ------------------------------------------ { "inline": "{\"query\":{\"bool\":{\"must\": {{#toJson}}clauses{{/toJson}} }}}", "params": { "clauses": [ { "term": "foo" }, { "term": "bar" } ] } } ------------------------------------------ which is rendered as: [source,js] ------------------------------------------ { "query" : { "bool" : { "must" : [ { "term" : "foo" }, { "term" : "bar" } ] } } } ------------------------------------------ [float] ===== Conditional clauses Conditional clauses cannot be expressed using the JSON form of the template. Instead, the template *must* be passed as a string. For instance, let's say we wanted to run a `match` query on the `line` field, and optionally wanted to filter by line numbers, where `start` and `end` are optional. The `params` would look like: [source,js] ------------------------------------------ { "params": { "text": "words to search for", "line_no": { <1> "start": 10, <1> "end": 20 <1> } } } ------------------------------------------ <1> All three of these elements are optional. We could write the query as: [source,js] ------------------------------------------ { "query": { "bool": { "must": { "match": { "line": "{{text}}" <1> } }, "filter": { {{#line_no}} <2> "range": { "line_no": { {{#start}} <3> "gte": "{{start}}" <4> {{#end}},{{/end}} <5> {{/start}} <3> {{#end}} <6> "lte": "{{end}}" <7> {{/end}} <6> } } {{/line_no}} <2> } } } } ------------------------------------------ <1> Fill in the value of param `text` <2> Include the `range` filter only if `line_no` is specified <3> Include the `gte` clause only if `line_no.start` is specified <4> Fill in the value of param `line_no.start` <5> Add a comma after the `gte` clause only if `line_no.start` AND `line_no.end` are specified <6> Include the `lte` clause only if `line_no.end` is specified <7> Fill in the value of param `line_no.end` [NOTE] ================================== As written above, this template is not valid JSON because it includes the _section_ markers like `{{#line_no}}`. For this reason, the template should either be stored in a file (see <>) or, when used via the REST API, should be written as a string: [source,js] -------------------- "inline": "{\"query\":{\"bool\":{\"must\":{\"match\":{\"line\":\"{{text}}\"}},\"filter\":{{{#line_no}}\"range\":{\"line_no\":{{{#start}}\"gte\":\"{{start}}\"{{#end}},{{/end}}{{/start}}{{#end}}\"lte\":\"{{end}}\"{{/end}}}}{{/line_no}}}}}}" -------------------- ================================== [float] [[pre-registered-templates]] ===== Pre-registered template You can register search templates by storing it in the `config/scripts` directory, in a file using the `.mustache` extension. In order to execute the stored template, reference it by it's name under the `template` key: [source,js] ------------------------------------------ GET /_search/template { "file": "storedTemplate", <1> "params": { "query_string": "search for these words" } } ------------------------------------------ <1> Name of the query template in `config/scripts/`, i.e., `storedTemplate.mustache`. You can also register search templates by storing it in the cluster state. There are REST APIs to manage these indexed templates. [source,js] ------------------------------------------ POST /_search/template/ { "template": { "query": { "match": { "title": "{{query_string}}" } } } } ------------------------------------------ This template can be retrieved by [source,js] ------------------------------------------ GET /_search/template/ ------------------------------------------ which is rendered as: [source,js] ------------------------------------------ { "template": { "query": { "match": { "title": "{{query_string}}" } } } } ------------------------------------------ This template can be deleted by [source,js] ------------------------------------------ DELETE /_search/template/ ------------------------------------------ To use an indexed template at search time use: [source,js] ------------------------------------------ GET /_search/template { "id": "templateName", <1> "params": { "query_string": "search for these words" } } ------------------------------------------ <1> Name of the query template stored in the `.scripts` index. [float] ==== Validating templates A template can be rendered in a response with given parameters using [source,js] ------------------------------------------ GET /_render/template { "inline": { "query": { "terms": { "status": [ "{{#status}}", "{{.}}", "{{/status}}" ] } } }, "params": { "status": [ "pending", "published" ] } } ------------------------------------------ This call will return the rendered template: [source,js] ------------------------------------------ { "template_output": { "query": { "terms": { "status": [ <1> "pending", "published" ] } } } } ------------------------------------------ <1> `status` array has been populated with values from the `params` object. File and indexed templates can also be rendered by replacing `inline` with `file` or `id` respectively. For example, to render a file template [source,js] ------------------------------------------ GET /_render/template { "file": "my_template", "params": { "status": [ "pending", "published" ] } } ------------------------------------------ Pre-registered templates can also be rendered using [source,js] ------------------------------------------ GET /_render/template/ { "params": { "..." } } ------------------------------------------ [[multi-search-template]] == Multi Search Template The multi search template API allows to execute several search template requests within the same API using the `_msearch/template` endpoint. The format of the request is similar to the <> format: [source,js] -------------------------------------------------- header\n body\n header\n body\n -------------------------------------------------- The header part supports the same `index`, `types`, `search_type`, `preference`, and `routing` options as the usual Multi Search API. The body includes a search template body request and supports inline, stored and file templates. Here is an example: [source,js] -------------------------------------------------- $ cat requests {"index": "test"} {"inline": {"query": {"match": {"user" : "{{username}}" }}}, "params": {"username": "john"}} <1> {"index": "_all", "types": "accounts"} {"inline": {"query": {"{{query_type}}": {"name": "{{name}}" }}}, "params": {"query_type": "match_phrase_prefix", "name": "Smith"}} {"index": "_all"} {"id": "template_1", "params": {"query_string": "search for these words" }} <2> {"types": "users"} {"file": "template_2", "params": {"field_name": "fullname", "field_value": "john smith" }} <3> $ curl -XGET localhost:9200/_msearch/template --data-binary "@requests"; echo -------------------------------------------------- <1> Inline search template request <2> Search template request based on a stored template <3> Search template request based on a file template The response returns a `responses` array, which includes the search template response for each search template request matching its order in the original multi search template request. If there was a complete failure for that specific search template request, an object with `error` message will be returned in place of the actual search response.