使用Elasticsearch搜索模板简化查询
本文介绍Elasticsearch搜索模板,如何定义搜索模板、调用搜索模板,并通过示例进行说明。
1. 管理Elasticsearch搜索模板
Elasticsearch搜索模板与关系型数据库的存储过程类似。实际就是带变量的查询(使用Mustache模板语言),实际查询时使用模板参数替换变量。
下面示例定义搜索模板:
POST _scripts/<templateid>
{
"script": {
"lang": "mustache",
"source": {
"query": {
"match": {
"title": "{
{query_string}}"
}
}
}
}
}
<templateid>
为搜索模板ID,{
{query_string}}
是参数。
查看已定义的搜索模板:
GET _scripts/<templateid>
执行响应如下:
{
"script" : {
"lang" : "mustache",
"source" : "{\"query\":{\"match\":{\"title\":\"{
{query_string}}\"}}}",
"options": {
"content_type" : "application/json; charset=UTF-8"
}
},
"_id": "<templateid>",
"found": true
}
删除搜索模板:
DELETE _scripts/<templateid>
2. 调试Elasticsearch搜索模板
2.1. 使用搜索模板
GET _search/template
{
"id": "<templateid>",
"params": {
"query_string": "search for these words"
}
}
可以在某索引下使用搜索模板,需要指定参数。<templateid>
为之前定义搜索模板ID.
2.2. 验证搜索模板
GET _render/template
{
"source": "{ \"query\": { \"terms\": {
{#toJson}}statuses{
{/toJson}} }}",
"params": {
"statuses" : {
"status": [ "pending", "published" ]
}
}
}
通过_render服务渲染模板,输出结果如下:
{
"template_output": {
"query": {
"terms": {
"status": [
"pending", "published"
]
}
}
}
}
存储搜索模板也可以使用下面命令进行验证:
GET _render/template/<template_name>
{
"params": {
"..."
}
}
当然也可以在请求体内指定id参数。
2.3. 生成json字符串
使用{
{#toJson}}parameter{
{/toJson}}
指令可以把参数转成json形式,用于复杂查询:
GET _render/template
{
"source": "{ \"query\": { \"terms\": {
{#toJson}}statuses{
{/toJson}} }}",
"params": {
"statuses" : {
"status": [ "pending", "published" ]
}
}
}
实际结果应该为:
{
"query": {
"terms": {
"status": [
"pending","published"
]
}
}
}
下面是更复杂的示例:
GET _render/template
{
"source": "{\"query\":{\"bool\":{\"must\": {
{#toJson}}clauses{
{/toJson}} }}}",
"params": {
"clauses": [
{ "term": { "user" : "foo" } },
{ "term": { "user" : "bar" } }
]
}
}
渲染结果为:
{
"query" : {
"bool" : {
"must" : [
{
"term" : {
"user" : "foo"
}
},
{
"term" : {
"user" : "bar"
}
}
]
}
}
}
2.4. 连接数组值
使用{
{#join}}array{
{/join}}
指令可以连接数组的元素并使用逗号进行分隔:
GET _render/template
{
"source": {
"query": {
"match": {
"emails": "{
{#join}}emails{
{/join}}"
}
}
},
"params": {
"emails": [ "username@email.com", "lastname@email.com" ]
}
}
渲染结果为:
{
"query" : {
"match" : {
"emails" : "username@email.com,lastname@email.com"
}
}
}
也可以指定分隔符:
GET _render/template
{
"source": {
"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"]
}
}
}
渲染结果为:
{
"query" : {
"range" : {
"born" : {
"gte" : "2016",
"lte" : "31/12/2017",
"format" : "dd/MM/yyyy||yyyy"
}
}
}
}
2.5. 指定缺省值
可以使用{
{var}}{
{^var}}default{
{/var}}
指令指定缺失值:
{
"source": {
"query": {
"range": {
"line_no": {
"gte": "{
{start}}",
"lte": "{
{end}}{
{^end}}20{
{/end}}"
}
}
}
},
"params": { ... }
}
当params参数值为{ "start": 10, "end": 15 }
时解析结果为:
{
"range": {
"line_no": {
"gte": "10",
"lte": "15"
}
}
}
当params参数值为{ "start": 10 }
时解析结果为:
{
"range": {
"line_no": {
"gte": "10",
"lte": "20"
}
}
}
2.6. 条件表达式
条件子句不能使用json形式表达,模板必须作为字符串进行传输。假设我们需要在line字段上执行match查询,其中start和end作为行号进行过滤是可选的。参数如下:
{
"params": {
"text": "words to search for",
"line_no": {
"start": 10,
"end": 20
}
}
}
对应查询模板:
{
"query": {
"bool": {
"must": {
"match": {
"line": "{
{text}}"
}
},
"filter": {
{
{#line_no}}
"range": {
"line_no": {
{
{#start}}
"gte": "{
{start}}"
{
{#end}},{
{/end}}
{
{/start}}
{
{#end}}
"lte": "{
{end}}"
{
{/end}}
}
}
{
{/line_no}}
}
}
}
}
{
{#line_no}}
表示如果line_no存在,则解析{
{/line_no}}
之间的内容。块内的模板语法也类似,都是实现了条件判断功能,伪代码表达如下:
if 条件 then
表达式
end if
实际应用可能会用到if else的功能
,在模板中的语法为:{
{^line_no}}
。
2.7. URL编码
{
{#url}}value{
{/url}}
指令能以HTML编码形式对字符串值进行编码,下面示例对url编码:
GET _render/template
{
"source" : {
"query" : {
"term": {
"http_access_log": "{
{#url}}{
{host}}/{
{page}}{
{/url}}"
}
}
},
"params": {
"host": "https://www.elastic.co/",
"page": "learn"
}
}
响应结果如下:
{
"template_output" : {
"query" : {
"term" : {
"http_access_log" : "https%3A%2F%2Fwww.elastic.co%2F%2Flearn"
}
}
}
}
3. Java Api 调用模板
SearchTemplateRequest request = new SearchTemplateRequest();
request.setRequest(new SearchRequest("posts"));
request.setScriptType(ScriptType.STORED);
request.setScript("title_search");
Map<String, Object> params = new HashMap<>();
params.put("field", "title");
params.put("value", "elasticsearch");
params.put("size", 5);
request.setScriptParams(params);
首先定义SearchTemplateRequest,然后设置查询索引,关联模板脚本,最后给查询指定参数。模板脚本也可以通过low-level REST client进行添加,当然也可以在kibana中直接添加。
也可以指定内联脚本,这是脚本自行管理,最终以INLINE方式指定即可。
SearchTemplateRequest request = new SearchTemplateRequest();
request.setRequest(new SearchRequest("posts"));
request.setScriptType(ScriptType.INLINE);
request.setScript(
"{" +
" \"query\": { \"match\" : { \"{
{field}}\" : \"{
{value}}\" } }," +
" \"size\" : \"{
{size}}\"" +
"}");
Map<String, Object> scriptParams = new HashMap<>();
scriptParams.put("field", "title");
scriptParams.put("value", "elasticsearch");
scriptParams.put("size", 5);
request.setScriptParams(scriptParams);
4. 总结
本文介绍Elasticsearch搜索模板的定义、解析,以及如何利用搜索模板进行简化查询,也提供Java api示例进行说明。
本文参考链接:https://blog.csdn.net/neweastsun/article/details/102816963