Elasticsearch 的Search Templates(搜索模板)是一种强大的功能,允许用户预定义搜索查询的结构,并通过参数动态调整查询内容。这种方式在实际应用中非常有用,尤其是在需要根据用户输入或不同场景灵活调整查询逻辑时,同时又避免了直接暴露 Elasticsearch 查询语法给最终用户。以下是对 Elasticsearch 搜索模板的详细说明,结合了前面的翻译和示例代码。
1.什么是搜索模板?
搜索模板是一种预定义的搜索查询,它允许用户通过参数动态调整查询内容。模板使用Mustache语法编写,支持变量替换、条件逻辑和循环等功能。通过搜索模板,用户可以:
• 将用户输入安全地嵌入查询中,避免直接暴露查询语法。
• 在不修改应用程序代码的情况下,灵活调整查询逻辑。
• 提高查询的复用性和可维护性。
2.搜索模板的使用场景
2.1 用户输入安全封装
在许多应用中,用户输入需要作为查询的一部分执行。直接将用户输入嵌入查询可能导致语法错误或安全问题(如注入攻击)。搜索模板通过 Mustache 语法将用户输入作为参数传递,避免了这些问题。
2.2 动态调整查询逻辑
在某些场景下,查询逻辑需要根据条件动态变化。例如,根据用户是否选择某个过滤条件,动态调整查询范围。搜索模板通过条件逻辑(如`{{#condition}}...{{/condition}}`)实现了这一点。
2.3 查询复用
如果多个地方需要执行类似的查询,但查询参数不同,搜索模板可以将这些查询逻辑抽象为一个模板,通过传递不同的参数来实现复用。
3.搜索模板的核心概念
3.1 Mustache 语法
搜索模板基于Mustache语法,这是一种简单但功能强大的模板语言。以下是一些常用的 Mustache 语法:
• 变量替换:`{{variable}}`,在模板中用参数值替换变量。
• 条件逻辑:`{{#condition}}...{{/condition}}`和`{{^condition}}...{{/condition}}`,分别表示条件为真和条件为假时的逻辑。
• 循环:`{{#array}}...{{/array}}`,用于遍历数组并动态生成内容。
• 默认值:`{{variable}}{{^variable}}default{{/variable}}`,如果变量未定义,则使用默认值。
• 自定义分隔符:可以通过`{{=( )=}}`更改默认的分隔符。
3.2 模板的生命周期
-
创建模板:使用`PUT _scripts/<template_id>`将模板存储到 Elasticsearch 集群中。
-
渲染模板:通过`POST _render/template`或`GET _search/template`使用参数渲染模板。
-
执行查询:渲染后的模板可以直接作为查询请求发送到 Elasticsearch。
-
删除模板:使用`DELETE _scripts/<template_id>`删除不再需要的模板。
4.搜索模板的操作步骤
4.1 创建搜索模板
创建一个搜索模板时,需要指定模板的 ID 和内容。模板内容使用 Mustache 语法编写。例如:
```http
PUT _scripts/my-search-template
{
"script": {
"lang": "mustache",
"source": {
"query": {
"match": {
"message": "{{query_string}}"
}
},
"from": "{{from}}",
"size": "{{size}}"
}
}
}
```
4.2 渲染模板
在运行查询之前,可以使用`POST _render/template`API 测试模板的渲染结果。例如:
```http
POST _render/template
{
"id": "my-search-template",
"params": {
"query_string": "hello world",
"from": 0,
"size": 10
}
}
```
渲染结果是一个完整的查询请求体:
```json
{
"template_output": {
"query": {
"match": {
"message": "hello world"
}
},
"from": "0",
"size": "10"
}
}
```
4.3 执行带模板的搜索
使用模板执行搜索时,可以通过`GET _search/template`或`POST _search/template`API 发送请求。例如:
```http
GET my-index/_search/template
{
"id": "my-search-template",
"params": {
"query_string": "hello world",
"from": 0,
"size": 10
}
}
```
返回结果与普通搜索 API 相同:
```json
{
"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"
}
}
]
}
}
```
4.4 删除模板
不再需要时,可以通过以下 API 删除模板:
```http
DELETE _scripts/my-search-template
```
5.搜索模板的高级用法
5.1 条件逻辑
搜索模板支持条件逻辑,允许根据参数动态调整查询内容。例如:
```http
PUT _scripts/conditional-search
{
"script": {
"lang": "mustache",
"source": {
"query": {
"bool": {
"filter": [
{{#date_filter}}
{
"range": {
"@timestamp": {
"gte": "{{date_start}}",
"lte": "{{date_end}}"
}
}
},
{{/date_filter}}
{
"term": {
"user.id": "{{user_id}}"
}
}
]
}
}
}
}
}
```
如果`date_filter`为`true`,则会包含时间范围过滤条件;否则,只包含用户 ID 过滤条件。
当然!这段代码展示了如何在 Elasticsearch 的搜索模板中使用 Mustache 的条件逻辑来动态调整查询内容。以下是对这段代码的详细解释:
代码解析
1.PUT 请求的含义
• `PUT _scripts/conditional-search`:这是一个 HTTP PUT 请求,用于创建或更新一个名为`conditional-search`的搜索模板。
• `_scripts`是 Elasticsearch 的一个内置 API,用于管理存储的脚本(包括搜索模板)。
2.请求体的结构
• `"lang": "mustache"`:指定模板使用的语言是 Mustache。
• `"source"`:定义了模板的具体内容,即一个 Elasticsearch 查询。
3.模板的结构
模板定义了一个布尔查询(`bool`),其中包含两个过滤条件:
- 时间范围过滤条件(可选):
```mustache
{{#date_filter}}
{
"range": {
"@timestamp": {
"gte": "{{date_start}}",
"lte": "{{date_end}}"
}
}
},
{{/date_filter}}
```
• `{{#date_filter}}...{{/date_filter}}`:这是一个条件块,只有当`date_filter`参数为`true`时,才会包含这段范围查询。
• `{{date_start}}`和`{{date_end}}`:这两个变量分别表示时间范围的开始和结束时间,它们的值将在模板渲染时动态替换。
- 用户 ID 过滤条件(始终存在):
```mustache
{
"term": {
"user.id": "{{user_id}}"
}
}
```
• 这是一个固定的过滤条件,始终会包含在查询中。`{{user_id}}`是一个变量,表示用户的 ID,其值将在模板渲染时动态替换。
代码的作用
这段代码的作用是创建一个搜索模板,用于动态生成一个布尔查询,其中包含两个过滤条件:
-
时间范围过滤条件:如果`date_filter`参数为`true`,则会根据`date_start`和`date_end`参数过滤文档的时间戳字段`@timestamp`。
-
用户 ID 过滤条件:始终会根据`user_id`参数过滤文档的`user.id`字段。
通过这种方式,模板可以根据传入的参数动态调整查询逻辑,而无需硬编码。
示例:渲染和使用模板
假设我们已经创建了上述模板,接下来可以通过以下方式渲染和使用它。
渲染模板(包含时间范围过滤)
```http
POST _render/template
{
"id": "conditional-search",
"params": {
"date_filter": true,
"date_start": "2025-01-01",
"date_end": "2025-02-01",
"user_id": "user123"
}
}
```
渲染结果
渲染后的模板将输出一个完整的查询请求体:
```json
{
"template_output": {
"query": {
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "2025-01-01",
"lte": "2025-02-01"
}
}
},
{
"term": {
"user.id": "user123"
}
}
]
}
}
}
}
```
渲染模板(不包含时间范围过滤)
```http
POST _render/template
{
"id": "conditional-search",
"params": {
"date_filter": false,
"user_id": "user123"
}
}
```
渲染结果
渲染后的模板将输出一个完整的查询请求体:
```json
{
"template_output": {
"query": {
"bool": {
"filter": [
{
"term": {
"user.id": "user123"
}
}
]
}
}
}
}
```
执行查询
你可以直接将渲染后的查询发送到 Elasticsearch,执行搜索操作:
```http
GET my-index/_search/template
{
"id": "conditional-search",
"params": {
"date_filter": true,
"date_start": "2025-01-01",
"date_end": "2025-02-01",
"user_id": "user123"
}
}
```
为什么使用条件逻辑?
-
动态调整查询逻辑:通过条件逻辑,模板可以根据传入的参数动态调整查询内容,而无需修改模板本身。
-
灵活性:这种方式允许模板根据不同的场景灵活调整查询逻辑,例如根据用户是否选择了时间范围过滤条件。
-
避免硬编码:模板中的条件逻辑使得查询逻辑更加灵活,避免了硬编码,提高了模板的复用性。
总结
这段代码展示了如何在 Elasticsearch 的搜索模板中使用 Mustache 的条件逻辑来动态调整查询内容。通过`{{#date_filter}}...{{/date_filter}}`,模板可以根据`date_filter`参数的值动态包含或排除时间范围过滤条件。这种方式非常适合需要根据用户输入或动态参数调整查询逻辑的场景。
5.2 循环和数组
搜索模板支持对数组进行循环,动态生成查询内容。例如:
```http
PUT _scripts/array-search
{
"script": {
"lang": "mustache",
"source": {
"query": {
"terms": {
"tags": "{{#tags}}{{.}}{{^last}},{{/last}}{{/tags}}"
}
}
}
}
}
```
通过`{{#tags}}...{{/tags}}`循环数组,并使用`{{.}}`表示当前元素。`{{^last}},{{/last}}`用于避免尾随逗号问题。
当然!这段代码展示了如何在 Elasticsearch 的搜索模板中使用 Mustache 的循环功能来处理数组参数。以下是对这段代码的详细解释:
代码解析
1.PUT 请求的含义
• `PUT _scripts/array-search`:这是一个 HTTP PUT 请求,用于创建或更新一个名为`array-search`的搜索模板。
• `_scripts`是 Elasticsearch 的一个内置 API,用于管理存储的脚本(包括搜索模板)。
2.请求体的结构
• `"lang": "mustache"`:指定模板使用的语言是 Mustache。
• `"source"`:定义了模板的具体内容,即一个 Elasticsearch 查询。
3.模板的结构
模板定义了一个`terms`查询,用于匹配文档中的`tags`字段。关键部分是`{{#tags}}{{.}}{{^last}},{{/last}}{{/tags}}`,它通过 Mustache 的循环功能动态生成`terms`查询的值列表。
• `{{#tags}}...{{/tags}}`:这是一个循环区块,用于遍历`tags`数组中的每个元素。
• `{{.}}`:表示当前循环中的元素值。
• `{{^last}},{{/last}}`:这是一个条件判断,用于检查当前元素是否是数组的最后一个元素。如果不是最后一个元素,则在值后面添加一个逗号(`,`)。这是为了避免生成的 JSON 数组中出现尾随逗号,因为尾随逗号会导致 JSON 格式错误。
代码的作用
这段代码的作用是创建一个搜索模板,用于动态生成一个`terms`查询。`terms`查询允许你指定一个字段(如`tags`)的多个值,并返回包含这些值的文档。
模板的核心部分是:
```mustache
"tags": "{{#tags}}{{.}}{{^last}},{{/last}}{{/tags}}"
```
• `{{#tags}}...{{/tags}}`:循环遍历`tags`数组。
• `{{.}}`:输出当前元素的值。
• `{{^last}},{{/last}}`:如果当前元素不是最后一个元素,则在值后面添加逗号。
通过这种方式,模板可以动态生成一个逗号分隔的值列表,例如:
```json
"tags": ["tag1", "tag2", "tag3"]
```
示例:渲染和使用模板
假设我们已经创建了上述模板,接下来可以通过以下方式渲染和使用它。
渲染模板
```http
POST _render/template
{
"id": "array-search",
"params": {
"tags": ["tag1", "tag2", "tag3"]
}
}
```
渲染结果
渲染后的模板将输出一个完整的查询请求体:
```json
{
"template_output": {
"query": {
"terms": {
"tags": ["tag1", "tag2", "tag3"]
}
}
}
}
```
执行查询
你可以直接将渲染后的查询发送到 Elasticsearch,执行搜索操作:
```http
GET my-index/_search/template
{
"id": "array-search",
"params": {
"tags": ["tag1", "tag2", "tag3"]
}
}
```
为什么使用循环和条件判断?
-
动态生成查询:通过循环和条件判断,模板可以根据传入的参数动态生成查询内容,而无需硬编码。
-
避免 JSON 格式错误:通过`{{^last}},{{/last}}`,模板可以避免在生成的 JSON 数组中出现尾随逗号,确保查询的合法性。
-
灵活性:这种方式允许模板处理任意长度的数组,而无需修改模板本身。
总结
这段代码展示了如何在 Elasticsearch 的搜索模板中使用 Mustache 的循环功能来处理数组参数。通过`{{#tags}}...{{/tags}}`遍历数组,并使用`{{.}}`输出当前元素值,同时通过`{{^last}},{{/last}}`避免尾随逗号问题。这种方式可以动态生成`terms`查询,非常适合需要根据用户输入或动态参数调整查询逻辑的场景。
5.3 自定义分隔符
默认情况下,Mustache 使用双大括号`{{ }}`作为分隔符。如果需要,可以自定义分隔符,例如:
```http
PUT _scripts/custom-delimiter
{
"script": {
"lang": "mustache",
"source": """
{{=( )=}}
{
"query": {
"match": {
"message": "(query_string)"
}
}
}
(={{ }}=)
"""
}
}
```
当然!这段代码展示了如何在 Elasticsearch 的搜索模板中使用自定义分隔符。这是 Mustache 模板语言的一个高级功能,允许你更改默认的变量分隔符(通常是双大括号`{{ }}`)。以下是对这段代码的详细解释:
代码解析
1.PUT 请求的含义
• `PUT _scripts/custom-delimiter`:这是向 Elasticsearch 发送的 HTTP PUT 请求,用于创建或更新一个名为`custom-delimiter`的搜索模板。
• `_scripts`是 Elasticsearch 的一个内置 API,用于管理存储的脚本(包括搜索模板)。
2.请求体的结构
• `"lang": "mustache"`:指定模板使用的语言是 Mustache。Elasticsearch 的搜索模板默认使用 Mustache 语法。
• `"source"`:定义了模板的具体内容。
3.自定义分隔符的使用
在模板的`source`部分,代码如下:
```mustache
{{=( )=}}
{
"query": {
"match": {
"message": "(query_string)"
}
}
}
(={{ }}=)
```
• `{{=( )=}}`:这是一个特殊的 Mustache 标签,用于将默认的分隔符从`{{ }}`更改为`( )`。这意味着在接下来的模板内容中,变量和逻辑将使用`( )`来包裹,而不是默认的`{{ }}`。
• `(query_string)`:这是模板中需要动态替换的变量,使用了自定义分隔符`( )`。
• `(={{ }}=)`:这是一个结束标签,用于将分隔符切换回默认的`{{ }}`。虽然在这个简单的例子中没有后续使用默认分隔符的内容,但这是一个好的实践,确保模板的可扩展性。
代码的作用
这段代码的作用是创建一个搜索模板,其查询部分如下:
```json
{
"query": {
"match": {
"message": "(query_string)"
}
}
}
```
• 在这个模板中,`query_string`是一个动态参数,将在运行时通过`params`提供。
• 通过自定义分隔符`( )`,模板中的变量`(query_string)`在渲染时会被替换为实际的参数值。
为什么使用自定义分隔符?
-
避免冲突:在某些情况下,模板内容中可能已经包含了默认的分隔符`{{ }}`,这可能导致解析冲突。通过更改分隔符,可以避免这种冲突。
-
可读性:自定义分隔符可以根据模板的上下文选择更直观的符号,提高模板的可读性。
-
兼容性:在某些复杂的场景中,自定义分隔符可以更好地与其他工具或语言集成。
示例:渲染和使用模板
假设我们已经创建了上述模板,接下来可以通过以下方式渲染和使用它:
渲染模板
```http
POST _render/template
{
"id": "custom-delimiter",
"params": {
"query_string": "hello world"
}
}
```
渲染结果
渲染后的模板将输出一个完整的查询请求体:
```json
{
"template_output": {
"query": {
"match": {
"message": "hello world"
}
}
}
}
```
执行查询
你可以直接将渲染后的查询发送到 Elasticsearch,执行搜索操作:
```http
GET my-index/_search/template
{
"id": "custom-delimiter",
"params": {
"query_string": "hello world"
}
}
```
总结
这段代码展示了如何在 Elasticsearch 的搜索模板中使用自定义分隔符。通过更改默认的分隔符,可以避免冲突、提高可读性,并增强模板的灵活性。自定义分隔符是 Mustache 模板语言的一个高级功能,虽然在简单场景中可能不是必需的,但在复杂场景中非常有用。
6.搜索模板的限制
尽管搜索模板功能强大,但也有以下限制:
• 不支持 Partial(片段):Elasticsearch 的搜索模板不支持 Mustache 的片段功能。
• 性能开销:虽然模板渲染的开销通常较小,但在高并发场景下仍需注意。
• 调试复杂性:模板逻辑可能较为复杂,调试时需要仔细检查 Mustache 语法和参数传递。
7.总结
Elasticsearch 的搜索模板是一种灵活且强大的功能,适用于需要动态调整查询逻辑的场景。通过 Mustache 语法,用户可以安全地将用户输入嵌入查询中,同时避免直接暴露查询语法。搜索模板支持变量替换、条件逻辑和循环等功能,能够满足复杂的查询需求。然而,使用时需要注意模板的调试和性能优化。