使用Elasticsearch搜索模板简化查询

使用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示例进行说明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值