Search Template

edit

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.

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 online documentation of the mustache project.

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 scripting docs

More template examples

edit
Filling in a query string with a single value
edit
GET /_search/template
{
    "inline": {
        "query": {
            "match": {
                "title": "{{query_string}}"
            }
        }
    },
    "params": {
        "query_string": "search for these words"
    }
}
Converting parameters to JSON
edit

The {{#toJson}}parameter{{/toJson}} function can be used to convert parameters like maps and array to their JSON representation:

GET /_search/template
{
  "inline": "{ \"query\": { \"terms\": { \"status\": {{#toJson}}status{{/toJson}} }}}",
  "params": {
    "status": [ "pending", "published" ]
  }
}

which is rendered as:

{
  "query": {
    "terms": {
      "status": [
        "pending",
        "published"
      ]
    }
  }
}

A more complex example substitutes an array of JSON objects:

{
    "inline": "{\"query\":{\"bool\":{\"must\": {{#toJson}}clauses{{/toJson}} }}}",
    "params": {
        "clauses": [
            { "term": "foo" },
            { "term": "bar" }
        ]
   }
}

which is rendered as:

{
    "query" : {
      "bool" : {
        "must" : [
          {
            "term" : "foo"
          },
          {
            "term" : "bar"
          }
        ]
      }
    }
}
Concatenating array of values
edit

The {{#join}}array{{/join}} function can be used to concatenate the values of an array as a comma delimited string:

GET /_search/template
{
  "inline": {
    "query": {
      "match": {
        "emails": "{{#join}}emails{{/join}}"
      }
    }
  },
  "params": {
    "emails": [ "username@email.com", "lastname@email.com" ]
  }
}

which is rendered as:

{
    "query" : {
        "match" : {
            "emails" : "username@email.com,lastname@email.com"
        }
    }
}

The function also accepts a custom delimiter:

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:

{
    "query" : {
      "range" : {
        "born" : {
          "gte" : "2016",
          "lte" : "31/12/2017",
          "format" : "dd/MM/yyyy||yyyy"
        }
      }
    }
}
Default values
edit

A default value is written as {{var}}{{^var}}default{{/var}} for instance:

{
  "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:

{
    "range": {
        "line_no": {
            "gte": "10",
            "lte": "15"
        }
  }
}

But when params is { "start": 10 } this query would use the default value for end:

{
    "range": {
        "line_no": {
            "gte": "10",
            "lte": "20"
        }
    }
}
Conditional clauses
edit

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:

{
    "params": {
        "text":      "words to search for",
        "line_no": { 
            "start": 10, 
            "end":   20  
        }
    }
}

All three of these elements are optional.

We could write the query as:

{
  "query": {
    "bool": {
      "must": {
        "match": {
          "line": "{{text}}" 
        }
      },
      "filter": {
        {{#line_no}} 
          "range": {
            "line_no": {
              {{#start}} 
                "gte": "{{start}}" 
                {{#end}},{{/end}} 
              {{/start}} 
              {{#end}} 
                "lte": "{{end}}" 
              {{/end}} 
            }
          }
        {{/line_no}} 
      }
    }
  }
}

Fill in the value of param text

Include the range filter only if line_no is specified

Include the gte clause only if line_no.start is specified

Fill in the value of param line_no.start

Add a comma after the gte clause only if line_no.start AND line_no.end are specified

Include the lte clause only if line_no.end is specified

Fill in the value of param line_no.end

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 Pre-registered template) or, when used via the REST API, should be written as a string:

"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}}}}}}"
Pre-registered template
edit

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:

GET /_search/template
{
    "file": "storedTemplate", 
    "params": {
        "query_string": "search for these words"
    }
}

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.

POST /_search/template/<templatename>
{
    "template": {
        "query": {
            "match": {
                "title": "{{query_string}}"
            }
        }
    }
}

This template can be retrieved by

GET /_search/template/<templatename>

which is rendered as:

{
    "template": {
        "query": {
            "match": {
                "title": "{{query_string}}"
            }
        }
    }
}

This template can be deleted by

DELETE /_search/template/<templatename>

To use an indexed template at search time use:

GET /_search/template
{
    "id": "templateName", 
    "params": {
        "query_string": "search for these words"
    }
}

Name of the query template stored in the .scripts index.

Validating templates

edit

A template can be rendered in a response with given parameters using

GET /_render/template
{
  "inline": {
    "query": {
      "terms": {
        "status": [
          "{{#status}}",
          "{{.}}",
          "{{/status}}"
        ]
      }
    }
  },
  "params": {
    "status": [ "pending", "published" ]
  }
}

This call will return the rendered template:

{
  "template_output": {
    "query": {
      "terms": {
        "status": [ 
          "pending",
          "published"
        ]
      }
    }
  }
}

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

GET /_render/template
{
  "file": "my_template",
  "params": {
    "status": [ "pending", "published" ]
  }
}

Pre-registered templates can also be rendered using

GET /_render/template/<template_name>
{
  "params": {
    "..."
  }
}