搜索模板

本节我们主要讨论怎么优化查询的构造方式,前面章节提到我们的一个 QueryDSL 是一个复杂的 JSON,我们每次在构造查询的时候,都需要进行各种参数的处理,然后再拼接出最终的 QueryDSL,首先是处理逻辑会变得很复杂,需要处理每种参数赋值与否的各种场景,最头疼的是,每次加参数的时候,都需要重新修改拼装逻辑,实在是头痛,那么有没有一种简洁优雅的方法来处理查询的构建呢。

答案自然是有的,那就是使用搜索模板(search_template),具体怎么使用呢?我们一起来看一下。

将查询和参数分离

Elasticsearch 里面的 Search Template 允许我们提前定义好一个查询的模板,并且通过参数的方式来接受外部传进来的变量,然后借助 Elasticsearch 内置的 Mustache 脚本引擎来进行渲染,从而得到最终的 QueryDSL,借助搜索模板,我们从而进行查询条件和具体查询语句的分离解耦。

先来看一个简单的通过搜索模板来执行查询的例子,如下图:

GET _search/template
{
    "source" : {
      "query": { "match" : { "{{my_field}}" : "{{my_value}}" } },
      "size" : "{{my_size}}"
    },
    "params" : {
        "my_field" : "name",
        "my_value" : "张三",
        "my_size" : 10
    }
}

这个 JSON 请求体主要包含两个参数,一个是 source 用来定义我们的模板,里面可以看到 query、size 都是我们常见的查询参数,比如这里使用的是 match 查询,字段参数是 my_field,{{}} 是 mustache 的语法,表示这个是引用的一个变量名,那么变量是怎么来的呢,我们看到下面有一个 params 节点,下面就是定义了这些变量的值。我们这个模板最后渲染的查询条件为:

{
      "query": { "match" : { "name" : "张三" } },
      "size" : "10"
 }

我们通过执行 _search/template 这个 API 就能够动态传参并渲染查询条件从而得到查询结果。 不过如果还是需要传完整的这样一个模板定义,那么还是没有达到我们的目的啊,好在 Elasticsearch 早想到了,我们可以把上面的模板单独存在一个地方,在使用的时候,通过模板 ID 引用就可以了。

小知识

  • 调试模板渲染结果: GET _render/template
  • 取回模板定义的语法: GET _scripts/<templatename>
  • 删除模板定义的语法: DELETE _scripts/<templatename>
  • 模板变量的输出:{{query_string}}
  • 判断模板变量:{{#line_no}}中间为当变量存在会输出的代码块{{/line_no}}

功能集成

还是延续社区文章数据的例子,我们先创建一个搜索模板对象,模板 ID 我们加上版本号,为了方便以后支持不同的版本,我们将该搜索模板取名为 forum_search_template_v1 ,如下:

POST _scripts/forum_search_template_v1
{
  "script": {
    "lang": "mustache",
    "source": {
      "size": "{{size}}",
      "query": {
        "match": {
          "{{field}}": "{{query}}"
        }
      },
      "_source": [
        "title",
        "id",
        "uid",
        "views"
      ]
    }
  }
}

现在我们看看如何来调用这个模板,并动态的传参,示例如下:

GET forum-mysql/_search/template
{
  "id": "forum_search_template_v1",
  "params": {
    "field": "title",
    "query": "elasticsearch",
    "size": 10
  }
}

小知识

  • 通过设置一个或者多个索引来应用索引模板的方法:GET index1,index2*/_search/template

上面的这个查询看起来是不是很清爽,执行上面的语句就能得到我们的查询结果了,更重要的是我们实现了查询条件和参数的分离。

接下来,我们只需要修改我们的 PHP 代码,替换之前硬编码的 QueryDSL 查询为我们新的模板调用就好了,如下图:

由于我们的 URL 是来自于后台的动态设置,我们同样需要进行相应的修改就可以了,如下图:

就算以后我们需要调整查询条件,我们只需要动态的修改搜索模板就好了,网站代码完全不用调整和重新部署,实在是非常的方便啊。

小知识

  • 设置模板参数的默认值 {{var}}{{^var}}default{{/var}}
  • 相关变量定义因为包含大括号 {{var}} 在source里面必须是包含在双引号内,否则请求体会变成不合法的 JSON,或者可以对整个 source 进行转义。
  • 将数组或者复杂对象直接转换为 JSON 对象 {{#toJson}}parameter{{/toJson}}
  • 将数组拼接成字符拼接的字符串 {{#join delimiter='||'}}date.formats{{/join delimiter='||'}}