Skip to main content
 首页 » 编程设计

使用Elasticsearch搜索模板简化查询

2022年07月19日140lori

使用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
阅读延展