搜索模板

编辑

搜索模板是一种存储的搜索,您可以使用不同的变量来运行它。

如果您使用 Elasticsearch 作为搜索后端,您可以将搜索栏中的用户输入作为搜索模板的参数传递。这使您可以在不向用户公开 Elasticsearch 的查询语法的情况下运行搜索。

如果您将 Elasticsearch 用于自定义应用程序,搜索模板允许您更改搜索,而无需修改应用程序的代码。

创建搜索模板

编辑

要创建或更新搜索模板,请使用 创建存储的脚本 API

请求的 source 支持与 搜索 API 的请求正文相同的参数。source 还接受来自开源项目 mustache.javaMustache 变量。

通常,Mustache 变量包含在双花括号中:{{my-var}}。当您运行模板化搜索时,Elasticsearch 会将这些变量替换为 params 中的值。要了解有关 mustache 语法的更多信息,请参阅 Mustache.js 手册。搜索模板必须使用 mustachelang

以下请求创建一个 idmy-search-template 的搜索模板。

resp = client.put_script(
    id="my-search-template",
    script={
        "lang": "mustache",
        "source": {
            "query": {
                "match": {
                    "message": "{{query_string}}"
                }
            },
            "from": "{{from}}",
            "size": "{{size}}"
        }
    },
)
print(resp)
response = client.put_script(
  id: 'my-search-template',
  body: {
    script: {
      lang: 'mustache',
      source: {
        query: {
          match: {
            message: '{{query_string}}'
          }
        },
        from: '{{from}}',
        size: '{{size}}'
      }
    }
  }
)
puts response
const response = await client.putScript({
  id: "my-search-template",
  script: {
    lang: "mustache",
    source: {
      query: {
        match: {
          message: "{{query_string}}",
        },
      },
      from: "{{from}}",
      size: "{{size}}",
    },
  },
});
console.log(response);
PUT _scripts/my-search-template
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "match": {
          "message": "{{query_string}}"
        }
      },
      "from": "{{from}}",
      "size": "{{size}}"
    }
  }
}

Elasticsearch 将搜索模板作为 Mustache 脚本存储在集群状态中。Elasticsearch 在 template 脚本上下文中编译搜索模板。限制或禁用脚本的设置也会影响搜索模板。

验证搜索模板

编辑

要使用不同的 params 测试模板,请使用 渲染搜索模板 API

resp = client.render_search_template(
    id="my-search-template",
    params={
        "query_string": "hello world",
        "from": 20,
        "size": 10
    },
)
print(resp)
response = client.render_search_template(
  body: {
    id: 'my-search-template',
    params: {
      query_string: 'hello world',
      from: 20,
      size: 10
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  id: "my-search-template",
  params: {
    query_string: "hello world",
    from: 20,
    size: 10,
  },
});
console.log(response);
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 来测试内联模板。

resp = client.render_search_template(
    source={
        "query": {
            "match": {
                "message": "{{query_string}}"
            }
        },
        "from": "{{from}}",
        "size": "{{size}}"
    },
    params={
        "query_string": "hello world",
        "from": 20,
        "size": 10
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: {
      query: {
        match: {
          message: '{{query_string}}'
        }
      },
      from: '{{from}}',
      size: '{{size}}'
    },
    params: {
      query_string: 'hello world',
      from: 20,
      size: 10
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: {
    query: {
      match: {
        message: "{{query_string}}",
      },
    },
    from: "{{from}}",
    size: "{{size}}",
  },
  params: {
    query_string: "hello world",
    from: 20,
    size: 10,
  },
});
console.log(response);
POST _render/template
{
    "source": {
      "query": {
        "match": {
          "message": "{{query_string}}"
        }
      },
      "from": "{{from}}",
      "size": "{{size}}"
    },
  "params": {
    "query_string": "hello world",
    "from": 20,
    "size": 10
  }
}

运行模板化搜索

编辑

要使用搜索模板运行搜索,请使用 搜索模板 API。您可以在每个请求中指定不同的 params

resp = client.search_template(
    index="my-index",
    id="my-search-template",
    params={
        "query_string": "hello world",
        "from": 0,
        "size": 10
    },
)
print(resp)
response = client.search_template(
  index: 'my-index',
  body: {
    id: 'my-search-template',
    params: {
      query_string: 'hello world',
      from: 0,
      size: 10
    }
  }
)
puts response
const response = await client.searchTemplate({
  index: "my-index",
  id: "my-search-template",
  params: {
    query_string: "hello world",
    from: 0,
    size: 10,
  },
});
console.log(response);
GET my-index/_search/template
{
  "id": "my-search-template",
  "params": {
    "query_string": "hello world",
    "from": 0,
    "size": 10
  }
}

响应使用与 搜索 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"
        }
      }
    ]
  }
}

运行多个模板化搜索

编辑

要使用单个请求运行多个模板化搜索,请使用 多搜索模板 API。与多个单独的搜索相比,这些请求通常具有更少的开销和更快的速度。

resp = client.msearch_template(
    index="my-index",
    search_templates=[
        {},
        {
            "id": "my-search-template",
            "params": {
                "query_string": "hello world",
                "from": 0,
                "size": 10
            }
        },
        {},
        {
            "id": "my-other-search-template",
            "params": {
                "query_type": "match_all"
            }
        }
    ],
)
print(resp)
response = client.msearch_template(
  index: 'my-index',
  body: [
    {},
    {
      id: 'my-search-template',
      params: {
        query_string: 'hello world',
        from: 0,
        size: 10
      }
    },
    {},
    {
      id: 'my-other-search-template',
      params: {
        query_type: 'match_all'
      }
    }
  ]
)
puts response
const response = await client.msearchTemplate({
  index: "my-index",
  search_templates: [
    {},
    {
      id: "my-search-template",
      params: {
        query_string: "hello world",
        from: 0,
        size: 10,
      },
    },
    {},
    {
      id: "my-other-search-template",
      params: {
        query_type: "match_all",
      },
    },
  ],
});
console.log(response);
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" }}

获取搜索模板

编辑

要检索搜索模板,请使用 获取存储的脚本 API

resp = client.get_script(
    id="my-search-template",
)
print(resp)
response = client.get_script(
  id: 'my-search-template'
)
puts response
const response = await client.getScript({
  id: "my-search-template",
});
console.log(response);
GET _scripts/my-search-template

要获取所有搜索模板和其他存储的脚本的列表,请使用 集群状态 API

resp = client.cluster.state(
    metric="metadata",
    pretty=True,
    filter_path="metadata.stored_scripts",
)
print(resp)
response = client.cluster.state(
  metric: 'metadata',
  pretty: true,
  filter_path: 'metadata.stored_scripts'
)
puts response
const response = await client.cluster.state({
  metric: "metadata",
  pretty: "true",
  filter_path: "metadata.stored_scripts",
});
console.log(response);
GET _cluster/state/metadata?pretty&filter_path=metadata.stored_scripts

删除搜索模板

编辑

要删除搜索模板,请使用 删除存储的脚本 API

resp = client.delete_script(
    id="my-search-template",
)
print(resp)
response = client.delete_script(
  id: 'my-search-template'
)
puts response
const response = await client.deleteScript({
  id: "my-search-template",
});
console.log(response);
DELETE _scripts/my-search-template

设置默认值

编辑

要为变量设置默认值,请使用以下语法

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

如果模板化搜索未在其 params 中指定值,则搜索将使用默认值。例如,以下模板为 fromsize 设置默认值。

resp = client.render_search_template(
    source={
        "query": {
            "match": {
                "message": "{{query_string}}"
            }
        },
        "from": "{{from}}{{^from}}0{{/from}}",
        "size": "{{size}}{{^size}}10{{/size}}"
    },
    params={
        "query_string": "hello world"
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: {
      query: {
        match: {
          message: '{{query_string}}'
        }
      },
      from: '{{from}}{{^from}}0{{/from}}',
      size: '{{size}}{{^size}}10{{/size}}'
    },
    params: {
      query_string: 'hello world'
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: {
    query: {
      match: {
        message: "{{query_string}}",
      },
    },
    from: "{{from}}{{^from}}0{{/from}}",
    size: "{{size}}{{^size}}10{{/size}}",
  },
  params: {
    query_string: "hello world",
  },
});
console.log(response);
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}} 函数对字符串进行 URL 编码。

resp = client.render_search_template(
    source={
        "query": {
            "term": {
                "url.full": "{{#url}}{{host}}/{{page}}{{/url}}"
            }
        }
    },
    params={
        "host": "http://example.com",
        "page": "hello-world"
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: {
      query: {
        term: {
          'url.full' => '{{#url}}{{host}}/{{page}}{{/url}}'
        }
      }
    },
    params: {
      host: 'http://example.com',
      page: 'hello-world'
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: {
    query: {
      term: {
        "url.full": "{{#url}}{{host}}/{{page}}{{/url}}",
      },
    },
  },
  params: {
    host: "http://example.com",
    page: "hello-world",
  },
});
console.log(response);
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}} 函数将数组值连接为逗号分隔的字符串。例如,以下模板连接两个电子邮件地址。

resp = client.render_search_template(
    source={
        "query": {
            "match": {
                "user.group.emails": "{{#join}}emails{{/join}}"
            }
        }
    },
    params={
        "emails": [
            "[email protected]",
            "[email protected]"
        ]
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: {
      query: {
        match: {
          'user.group.emails' => '{{#join}}emails{{/join}}'
        }
      }
    },
    params: {
      emails: [
        '[email protected]',
        '[email protected]'
      ]
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: {
    query: {
      match: {
        "user.group.emails": "{{#join}}emails{{/join}}",
      },
    },
  },
  params: {
    emails: ["[email protected]", "[email protected]"],
  },
});
console.log(response);
POST _render/template
{
  "source": {
    "query": {
      "match": {
        "user.group.emails": "{{#join}}emails{{/join}}"
      }
    }
  },
  "params": {
    "emails": [ "[email protected]", "[email protected]" ]
  }
}

模板渲染为

{
  "template_output": {
    "query": {
      "match": {
        "user.group.emails": "[email protected],[email protected]"
      }
    }
  }
}

您还可以指定自定义分隔符。

resp = client.render_search_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"
            ]
        }
    },
)
print(resp)
response = client.render_search_template(
  body: {
    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'
        ]
      }
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  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"],
    },
  },
});
console.log(response);
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}} 函数将变量值转换为其 JSON 表示形式。

例如,以下模板使用 {{#toJson}} 传递数组。为确保请求正文是有效的 JSON,source 以字符串格式编写。

resp = client.render_search_template(
    source="{ \"query\": { \"terms\": { \"tags\": {{#toJson}}tags{{/toJson}} }}}",
    params={
        "tags": [
            "prod",
            "es01"
        ]
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: '{ "query": { "terms": { "tags": {{#toJson}}tags{{/toJson}} }}}',
    params: {
      tags: [
        'prod',
        'es01'
      ]
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: '{ "query": { "terms": { "tags": {{#toJson}}tags{{/toJson}} }}}',
  params: {
    tags: ["prod", "es01"],
  },
});
console.log(response);
POST _render/template
{
  "source": "{ \"query\": { \"terms\": { \"tags\": {{#toJson}}tags{{/toJson}} }}}",
  "params": {
    "tags": [
      "prod",
      "es01"
    ]
  }
}

模板渲染为

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

您还可以使用 {{#toJson}} 传递对象。

resp = client.render_search_template(
    source="{ \"query\": {{#toJson}}my_query{{/toJson}} }",
    params={
        "my_query": {
            "match_all": {}
        }
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: '{ "query": {{#toJson}}my_query{{/toJson}} }',
    params: {
      my_query: {
        match_all: {}
      }
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: '{ "query": {{#toJson}}my_query{{/toJson}} }',
  params: {
    my_query: {
      match_all: {},
    },
  },
});
console.log(response);
POST _render/template
{
  "source": "{ \"query\": {{#toJson}}my_query{{/toJson}} }",
  "params": {
    "my_query": {
      "match_all": { }
    }
  }
}

模板渲染为

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

您还可以传递对象数组。

resp = client.render_search_template(
    source="{ \"query\": { \"bool\": { \"must\": {{#toJson}}clauses{{/toJson}} }}}",
    params={
        "clauses": [
            {
                "term": {
                    "user.id": "kimchy"
                }
            },
            {
                "term": {
                    "url.domain": "example.com"
                }
            }
        ]
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: '{ "query": { "bool": { "must": {{#toJson}}clauses{{/toJson}} }}}',
    params: {
      clauses: [
        {
          term: {
            'user.id' => 'kimchy'
          }
        },
        {
          term: {
            'url.domain' => 'example.com'
          }
        }
      ]
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: '{ "query": { "bool": { "must": {{#toJson}}clauses{{/toJson}} }}}',
  params: {
    clauses: [
      {
        term: {
          "user.id": "kimchy",
        },
      },
      {
        term: {
          "url.domain": "example.com",
        },
      },
    ],
  },
});
console.log(response);
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,则以下模板会搜索过去一年的数据。

resp = client.render_search_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"
    },
)
print(resp)
response = client.render_search_template(
  body: {
    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'
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  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",
  },
});
console.log(response);
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,则模板会搜索任何时间段的数据。

resp = client.render_search_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"
    },
)
print(resp)
response = client.render_search_template(
  body: {
    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'
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  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",
  },
});
console.log(response);
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,则以下模板会搜索过去一年的数据。否则,它会搜索过去一天的数据。

resp = client.render_search_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"
    },
)
print(resp)
response = client.render_search_template(
  body: {
    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'
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  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",
  },
});
console.log(response);
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 的搜索模板示例

编辑

mustache 模板语言定义了您可以在模板中使用的各种标记类型。以下各节描述了其中一些标记类型,并提供了在 Elasticsearch 搜索模板中使用它们的示例。

Mustache 变量

编辑

Mustache 标记通常包含在双花括号中。Mustache 变量:{{my-variable}} 是一种 Mustache 标记。当您运行模板化搜索时,Elasticsearch 会将这些变量替换为 params 中的值。

例如,考虑以下搜索模板

resp = client.put_script(
    id="my-search-template",
    script={
        "lang": "mustache",
        "source": {
            "query": {
                "match": {
                    "message": "{{query_string}}"
                }
            },
            "from": "{{from}}",
            "size": "{{size}}"
        }
    },
)
print(resp)
response = client.put_script(
  id: 'my-search-template',
  body: {
    script: {
      lang: 'mustache',
      source: {
        query: {
          match: {
            message: '{{query_string}}'
          }
        },
        from: '{{from}}',
        size: '{{size}}'
      }
    }
  }
)
puts response
const response = await client.putScript({
  id: "my-search-template",
  script: {
    lang: "mustache",
    source: {
      query: {
        match: {
          message: "{{query_string}}",
        },
      },
      from: "{{from}}",
      size: "{{size}}",
    },
  },
});
console.log(response);
PUT _scripts/my-search-template
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "match": {
          "message": "{{query_string}}"
        }
      },
      "from": "{{from}}",
      "size": "{{size}}"
    }
  }
}

使用 params 测试上述搜索模板

resp = client.render_search_template(
    id="my-search-template",
    params={
        "query_string": "hello world",
        "from": 20,
        "size": 10
    },
)
print(resp)
response = client.render_search_template(
  body: {
    id: 'my-search-template',
    params: {
      query_string: 'hello world',
      from: 20,
      size: 10
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  id: "my-search-template",
  params: {
    query_string: "hello world",
    from: 20,
    size: 10,
  },
});
console.log(response);
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,则消息将被替换为空字符串。

例如

resp = client.render_search_template(
    id="my-search-template",
    params={
        "from": 20,
        "size": 10
    },
)
print(resp)
response = client.render_search_template(
  body: {
    id: 'my-search-template',
    params: {
      from: 20,
      size: 10
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  id: "my-search-template",
  params: {
    from: 20,
    size: 10,
  },
});
console.log(response);
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}} 结尾。

以下搜索模板显示了使用带有嵌套对象的节的示例

resp = client.render_search_template(
    source="\n  {\n    \"query\": {\n      \"match\": {\n        {{#query_message}}\n          {{#query_string}}\n        \"message\": \"Hello {{#first_name_section}}{{first_name}}{{/first_name_section}} {{#last_name_section}}{{last_name}}{{/last_name_section}}\"\n          {{/query_string}}\n        {{/query_message}}\n      }\n    }\n  }\n  ",
    params={
        "query_message": {
            "query_string": {
                "first_name_section": {
                    "first_name": "John"
                },
                "last_name_section": {
                    "last_name": "kimchy"
                }
            }
        }
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: "\n  {\n    \"query\": {\n      \"match\": {\n        {{#query_message}}\n          {{#query_string}}\n        \"message\": \"Hello {{#first_name_section}}{{first_name}}{{/first_name_section}} {{#last_name_section}}{{last_name}}{{/last_name_section}}\"\n          {{/query_string}}\n        {{/query_message}}\n      }\n    }\n  }\n  ",
    params: {
      query_message: {
        query_string: {
          first_name_section: {
            first_name: 'John'
          },
          last_name_section: {
            last_name: 'kimchy'
          }
        }
      }
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source:
    '\n  {\n    "query": {\n      "match": {\n        {{#query_message}}\n          {{#query_string}}\n        "message": "Hello {{#first_name_section}}{{first_name}}{{/first_name_section}} {{#last_name_section}}{{last_name}}{{/last_name_section}}"\n          {{/query_string}}\n        {{/query_message}}\n      }\n    }\n  }\n  ',
  params: {
    query_message: {
      query_string: {
        first_name_section: {
          first_name: "John",
        },
        last_name_section: {
          last_name: "kimchy",
        },
      },
    },
  },
});
console.log(response);
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"
      }
    }
  }
}
列表
编辑

您可以传递对象列表,并在搜索模板中循环访问每个项目。

例如,以下搜索模板结合了 并匹配所有用户名

resp = client.put_script(
    id="my-search-template",
    script={
        "lang": "mustache",
        "source": {
            "query": {
                "multi_match": {
                    "query": "{{query_string}}",
                    "fields": "[{{#text_fields}}{{user_name}},{{/text_fields}}]"
                }
            }
        }
    },
)
print(resp)
response = client.put_script(
  id: 'my-search-template',
  body: {
    script: {
      lang: 'mustache',
      source: {
        query: {
          multi_match: {
            query: '{{query_string}}',
            fields: '[{{#text_fields}}{{user_name}},{{/text_fields}}]'
          }
        }
      }
    }
  }
)
puts response
const response = await client.putScript({
  id: "my-search-template",
  script: {
    lang: "mustache",
    source: {
      query: {
        multi_match: {
          query: "{{query_string}}",
          fields: "[{{#text_fields}}{{user_name}},{{/text_fields}}]",
        },
      },
    },
  },
});
console.log(response);
PUT _scripts/my-search-template
{
  "script": {
    "lang": "mustache",
    "source": {
      "query":{
        "multi_match":{
          "query": "{{query_string}}",
          "fields": """[{{#text_fields}}{{user_name}},{{/text_fields}}]"""
        }
      }
    }
  }
}

测试模板

resp = client.render_search_template(
    id="my-search-template",
    params={
        "query_string": "My string",
        "text_fields": [
            {
                "user_name": "John"
            },
            {
                "user_name": "kimchy"
            }
        ]
    },
)
print(resp)
response = client.render_search_template(
  body: {
    id: 'my-search-template',
    params: {
      query_string: 'My string',
      text_fields: [
        {
          user_name: 'John'
        },
        {
          user_name: 'kimchy'
        }
      ]
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  id: "my-search-template",
  params: {
    query_string: "My string",
    text_fields: [
      {
        user_name: "John",
      },
      {
        user_name: "kimchy",
      },
    ],
  },
});
console.log(response);
POST _render/template
{
  "id": "my-search-template",
  "params": {
    "query_string": "My string",
    "text_fields": [
      {
        "user_name": "John"
      },
      {
        "user_name": "kimchy"
      }
    ]
  }
}

渲染后,模板输出

{
  "template_output": {
    "query": {
      "multi_match": {
        "query": "My string",
        "fields": "[John,kimchy,]"
      }
    }
  }
}

以上将导致尾随逗号问题,从而导致无效的 JSON。一种解决方法是包含一个 倒置节 并添加一个变量,以确保它是数组中的最后一个项目。

例如

resp = client.put_script(
    id="my-search-template",
    script={
        "lang": "mustache",
        "source": {
            "query": {
                "multi_match": {
                    "query": "{{query_string}}",
                    "fields": "[{{#text_fields}}{{user_name}}{{^last}},{{/last}}{{/text_fields}}]"
                }
            }
        }
    },
)
print(resp)
response = client.put_script(
  id: 'my-search-template',
  body: {
    script: {
      lang: 'mustache',
      source: {
        query: {
          multi_match: {
            query: '{{query_string}}',
            fields: '[{{#text_fields}}{{user_name}}{{^last}},{{/last}}{{/text_fields}}]'
          }
        }
      }
    }
  }
)
puts response
const response = await client.putScript({
  id: "my-search-template",
  script: {
    lang: "mustache",
    source: {
      query: {
        multi_match: {
          query: "{{query_string}}",
          fields:
            "[{{#text_fields}}{{user_name}}{{^last}},{{/last}}{{/text_fields}}]",
        },
      },
    },
  },
});
console.log(response);
PUT _scripts/my-search-template
{
  "script": {
    "lang": "mustache",
    "source": {
      "query":{
        "multi_match":{
          "query": "{{query_string}}",
          "fields": """[{{#text_fields}}{{user_name}}{{^last}},{{/last}}{{/text_fields}}]"""
        }
      }
    }
  }
}

再次使用变量:last 测试 my-search-template,以确定它是数组中的最后一个项目

resp = client.render_search_template(
    id="my-search-template",
    params={
        "query_string": "My string",
        "text_fields": [
            {
                "user_name": "John",
                "last": False
            },
            {
                "user_name": "kimchy",
                "last": True
            }
        ]
    },
)
print(resp)
response = client.render_search_template(
  body: {
    id: 'my-search-template',
    params: {
      query_string: 'My string',
      text_fields: [
        {
          user_name: 'John',
          last: false
        },
        {
          user_name: 'kimchy',
          last: true
        }
      ]
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  id: "my-search-template",
  params: {
    query_string: "My string",
    text_fields: [
      {
        user_name: "John",
        last: false,
      },
      {
        user_name: "kimchy",
        last: true,
      },
    ],
  },
});
console.log(response);
POST _render/template
{
  "id": "my-search-template",
  "params": {
    "query_string": "My string",
    "text_fields": [
      {
        "user_name": "John",
        "last": false
      },
      {
        "user_name": "kimchy",
        "last": true
      }
    ]
  }
}

渲染后,模板输出

{
  "template_output": {
    "query": {
      "multi_match": {
        "query": "My string",
        "fields": "[John,kimchy]"
      }
    }
  }
}
Lambda
编辑

Elasticsearch 具有预构建的自定义函数,以支持将文本转换为特定格式。

要了解有关使用 mustache lambda 的更多信息,请查看 Url 编码字符串连接值转换为 json 中的示例。

倒置节

编辑

当您只想设置一次值时,倒置节非常有用。

要使用倒置节,请使用以下语法

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

例如,在以下搜索模板中,如果 name_existsfalse,则 message 设置为 Hello World,否则将其设置为空字符串。

resp = client.render_search_template(
    source={
        "query": {
            "match": {
                "message": "{{^name_exists}}Hello World{{/name_exists}}"
            }
        }
    },
    params={
        "name_exists": False
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: {
      query: {
        match: {
          message: '{{^name_exists}}Hello World{{/name_exists}}'
        }
      }
    },
    params: {
      name_exists: false
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: {
    query: {
      match: {
        message: "{{^name_exists}}Hello World{{/name_exists}}",
      },
    },
  },
  params: {
    name_exists: false,
  },
});
console.log(response);
POST _render/template
{
  "source": {
    "query": {
      "match": {
        "message": "{{^name_exists}}Hello World{{/name_exists}}"
      }
    }
  },
  "params": {
     "name_exists": false
  }
}

它们还可以与 条件默认值 结合使用。

例如,在以下搜索模板中,如果 name_existstrue,则替换 {{query_string}} 的值。如果 name_existsfalse,则将其设置为默认值 World

resp = client.render_search_template(
    source={
        "query": {
            "match": {
                "message": "Hello {{#name_exists}}{{query_string}}{{/name_exists}}{{^name_exists}}World{{/name_exists}}"
            }
        }
    },
    params={
        "query_string": "Kimchy",
        "name_exists": True
    },
)
print(resp)
response = client.render_search_template(
  body: {
    source: {
      query: {
        match: {
          message: 'Hello {{#name_exists}}{{query_string}}{{/name_exists}}{{^name_exists}}World{{/name_exists}}'
        }
      }
    },
    params: {
      query_string: 'Kimchy',
      name_exists: true
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  source: {
    query: {
      match: {
        message:
          "Hello {{#name_exists}}{{query_string}}{{/name_exists}}{{^name_exists}}World{{/name_exists}}",
      },
    },
  },
  params: {
    query_string: "Kimchy",
    name_exists: true,
  },
});
console.log(response);
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)

resp = client.put_script(
    id="my-search-template",
    script={
        "lang": "mustache",
        "source": "\n    {\n      \"query\": {\n        \"match\": {\n           {{=( )=}}\n          \"message\": \"(query_string)\"\n          (={{ }}=)\n        }\n      }\n    }\n    "
    },
)
print(resp)
response = client.put_script(
  id: 'my-search-template',
  body: {
    script: {
      lang: 'mustache',
      source: "\n    {\n      \"query\": {\n        \"match\": {\n           {{=( )=}}\n          \"message\": \"(query_string)\"\n          (={{ }}=)\n        }\n      }\n    }\n    "
    }
  }
)
puts response
const response = await client.putScript({
  id: "my-search-template",
  script: {
    lang: "mustache",
    source:
      '\n    {\n      "query": {\n        "match": {\n           {{=( )=}}\n          "message": "(query_string)"\n          (={{ }}=)\n        }\n      }\n    }\n    ',
  },
});
console.log(response);
PUT _scripts/my-search-template
{
  "script": {
    "lang": "mustache",
    "source":
    """
    {
      "query": {
        "match": {
           {{=( )=}}
          "message": "(query_string)"
          (={{ }}=)
        }
      }
    }
    """
  }
}

使用新的分隔符测试模板

resp = client.render_search_template(
    id="my-search-template",
    params={
        "query_string": "hello world"
    },
)
print(resp)
response = client.render_search_template(
  body: {
    id: 'my-search-template',
    params: {
      query_string: 'hello world'
    }
  }
)
puts response
const response = await client.renderSearchTemplate({
  id: "my-search-template",
  params: {
    query_string: "hello world",
  },
});
console.log(response);
POST _render/template
{
  "id": "my-search-template",
  "params": {
    "query_string": "hello world"
  }
}

渲染后,模板输出

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

不支持的功能

编辑

以下 mustache 功能在 Elasticsearch 搜索模板中不受支持

  • 局部