乐趣区

关于后端:ES-Search-Template

ES Search Template

所谓 search template 搜寻模板其实就是:

  1. 事后定义好查问语句 DSL 的构造并预留参数
  2. 搜寻的时再传入参数值
  3. 渲染出残缺的 DSL,最初进行搜寻

应用搜寻模板能够将 DSL 从应用程序中解耦进去,并且能够更加灵便的更改查问语句。

例如:

GET _search/template
{
  "source" : {
    "query": {
      "match" : {"{{my_field}}" : "{{my_value}}"
      }
    }
  },
  "params" : {
    "my_field" : "message",
    "my_value" : "foo"
  }
}

结构进去的 DSL 就是:

{
  "query": {
    "match": {"message": "foo"}
  }
}

在模板中通过 {{}} 的形式预留参数,而后查问时再指定对应的参数值,最初填充成具体的查问语句进行搜寻。


搜寻模板 API

为了实现搜寻模板和查问拆散,咱们首先须要独自保留和治理搜寻模板。

保留搜寻模板

应用 scripts API 保留搜寻模板(不存在则创立,存在则笼罩)。示例:

POST _scripts/<templateid>
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
        "match": {"title": "{{query_string}}"
        }
      }
    }
  }
}
查问搜寻模板
GET _scripts/<templateid>
删除搜寻模板
DELETE _scripts/<templateid>
应用搜寻模板

示例:

GET _search/template
{
  "id": "<templateid>",
  "params": {"query_string": "search words"}
}

params 中的参数与搜寻模板中定义的统一,上文保留搜寻模板的示例是 {{query_string}},所以这里进行搜寻时对应的参数就是 query_string

测验搜寻模板

有时候咱们想看看搜寻模板输出了参数之后渲染成的 DSL 到底长啥样。

示例:

GET _render/template
{"source": "{ \"query\": { \"terms\": {{#toJson}}statuses{{/toJson}} }}",
  "params": {
    "statuses" : {"status": [ "pending", "published"]
    }
  }
}

返回的后果就是:

{
  "template_output": {
    "query": {
      "terms": {
        "status": [
          "pending",
          "published"
        ]
      }
    }
  }
}

{{#toJson}} {{/toJson}} 就是转换成 json 格局。

曾经保留的搜寻模板能够通过以下形式查看渲染后果:

GET _render/template/<template_name>
{
  "params": {"..."}
}
应用 explainprofile 参数

示例:

GET _search/template
{
  "id": "my_template",
  "params": {"status": [ "pending", "published"]
  },
  "explain": true
}
GET _search/template
{
  "id": "my_template",
  "params": {"status": [ "pending", "published"]
  },
  "profile": true
}

模板渲染

填充简略值
GET _search/template
{
  "source": {
    "query": {
      "term": {"message": "{{query_string}}"
      }
    }
  },
  "params": {"query_string": "search words"}
}

渲染进去的 DSL 就是:

{
  "query": {
    "term": {"message": "search words"}
  }
}
将参数转换为 JSON

应用 {{#toJson}}parameter{{/toJson}} 会将参数转换为 JSON。

GET _search/template
{"source": "{ \"query\": { \"terms\": {{#toJson}}statuses{{/toJson}} }}",
  "params": {
    "statuses" : {"status": [ "pending", "published"]
    }
  }
}

渲染进去的 DSL 就是:

{
  "query": {
    "terms": {
      "status": [
        "pending",
        "published"
      ]
    }
  }
}

对象数组的渲染示例:

GET _search/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"} }
      ]
    }
  }
}
将数组 join 成字符串

应用 {{#join}}array{{/join}} 能够将数组 join 成字符串。

示例:

GET _search/template
{
  "source": {
    "query": {
      "match": {"emails": "{{#join}}emails{{/join}}"
      }
    }
  },
  "params": {"emails": [ "aaa", "bbb"]
  }
}

渲染后果:

{
  "query" : {
    "match" : {"emails" : "aaa,bbb"}
  }
}

除了默认以 , 分隔外,还能够自定义分隔符,示例:

{
  "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"]
    }
  }
}

例子中的 {{#join delimiter='||'}} {{/join delimiter='||'}} 意思就是进行 join 操作,分隔符设置为 ||,渲染后果就是:

{
  "query": {
    "range": {
      "born": {
        "gte": "2016",
        "lte": "31/12/2017",
        "format": "dd/MM/yyyy||yyyy"
      }
    }
  }
}
默认值

应用 {{var}}{{^var}}default{{/var}} 的形式设置默认值。

示例:

{
  "source": {
    "query": {
      "range": {
        "line_no": {"gte": "{{start}}",
          "lte": "{{end}}{{^end}}20{{/end}}"
        }
      }
    }
  },
  "params": {...}
}

{{end}}{{^end}}20{{/end}} 就是给 end 设置了默认值为 20。

params{"start": 10, "end": 15} 时,渲染后果是:

{
  "range": {
    "line_no": {
      "gte": "10",
      "lte": "15"
    }
  }
}

params{"start": 10} 时,end 就会应用默认值,渲染后果就是:

{
  "range": {
    "line_no": {
      "gte": "10",
      "lte": "20"
    }
  }
}
条件子句

有时候咱们的参数是可选的,这时候就能够应用 {{#key}} {{/key}}的语法。

示例,假如参数 line_no, start, end 都是可选的,应用 {{#key}} {{/key}} 形如:

{
  "query": {
    "bool": {
      "must": {
        "match": {"line": "{{text}}"
        }
      },
      "filter": {{{#line_no}}
          "range": {
            "line_no": {{{#start}}
                "gte": "{{start}}"
                {{#end}},{{/end}}
              {{/start}}
              {{#end}}
                "lte": "{{end}}"
              {{/end}}
            }
          }
        {{/line_no}}
      }
    }
  }
}

1、当参数为:

{
  "params": {
    "text": "words to search for",
    "line_no": {
      "start": 10,
      "end": 20
    }
  }
}

渲染后果是:

{
  "query": {
    "bool": {
      "must": {
        "match": {"line": "words to search for"}
      },
      "filter": {
        "range": {
          "line_no": {
            "gte": "10",
            "lte": "20"
          }
        }
      }
    }
  }
}

2、当参数为:

{
  "params": {"text": "words to search for"}
}

渲染后果为:

{
  "query": {
    "bool": {
      "must": {
        "match": {"line": "words to search for"}
      },
      "filter": {}}
  }
}

3、当参数为:

{
  "params": {
    "text": "words to search for",
    "line_no": {"start": 10}
  }
}

渲染后果为:

{
  "query": {
    "bool": {
      "must": {
        "match": {"line": "words to search for"}
      },
      "filter": {
        "range": {
          "line_no": {"gte": 10}
        }
      }
    }
  }
}

4、当参数为:

{
  "params": {
    "text": "words to search for",
    "line_no": {"end": 20}
  }
}

渲染后果为:

{
  "query": {
    "bool": {
      "must": {
        "match": {"line": "words to search for"}
      },
      "filter": {
        "range": {
          "line_no": {"lte": 20}
        }
      }
    }
  }
}

须要留神的是在 JSON 对象中,

{
  "filter": {{{#line_no}}
    ...
    {{/line_no}}
  }
}

这样间接写 {{#line_no}} 必定是非法的 JSON 格局,你必须转换为 JSON 字符串。

URLs 编码

应用 {{#url}}value{{/url}} 的形式能够进行 HTML 编码本义。

示例:

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"}
    }
  }
}

Mustache 根本语法

上文中的 {{}} 语法其实就是 mustache language,补充介绍下根本的语法规定。

应用 {{key}}

模板:Hello {{name}}

输出:

{"name": "Chris"}

输入:Hello Chris

应用 {{{key}}} 防止本义

所有变量都会默认进行 HTML 本义。

模板:{{company}}

输出:

{"company": "<b>GitHub</b>"}

输入:&lt;b&gt;GitHub&lt;/b&gt;

应用 {{{}}} 防止本义。

模板:{{{company}}}

输出:

{"company": "<b>GitHub</b>"}

输入:<b>GitHub</b>

应用 {{#key}} {{/key}} 结构区块

1、当 key 是 false 或者空列表将会疏忽
模板:

    Shown.
    {{#person}}
        Never shown!
    {{/person}}

输出:

{"person": false}

输入:

    Shown.

2、当 key 非空值则渲染填充
模板:

    {{#repo}}
        <b>{{name}}</b>
    {{/repo}}

输出:

{
    "repo": [{ "name": "resque"},
        {"name": "hub"},
        {"name": "rip"}
    ]
}

输入:

    <b>resque</b>
    <b>hub</b>
    <b>rip</b>

3、当 key 是函数则调用后渲染
模板:

    {{#wrapped}}
        {{name}} is awesome.
    {{/wrapped}}

输出:

{
    "name": "Willy",
    "wrapped": function() {return function(text, render) {return "<b>" + render(text) + "</b>"
        }
    }
}

输入:

    <b>Willy is awesome.</b>

4、当 key 是非 false 且非列表
模板:

    {{#person?}}
        Hi {{name}}!
    {{/person?}}

输出:

{"person?": { "name": "Jon"}
}

输入:

    Hi Jon!
应用 {{^key}} {{/key}} 结构反区块

{{^key}} {{/key}} 的语法与 {{#key}} {{/key}} 相似,不同的是,当 key 不存在,或者是 false,又或者是空列表时才渲染输入区块内容。

模板:

    {{#repo}}
        <b>{{name}}</b>
    {{/repo}}
    {{^repo}}
        No repos :({{/repo}}

输出:

{"repo": []
}

输入:

    No repos :(
应用 {{!}} 增加正文

{{!}} 正文内容将会被疏忽。

模板:

<h1>Today{{! ignore me}}.</h1>

输入:

<h1>Today.</h1>
应用 {{>}} 子模块

模板:

base.mustache:
<h2>Names</h2>
{{#names}}
    {{> user}}
{{/names}}

user.mustache:
<strong>{{name}}</strong>

其实也就等价于:

<h2>Names</h2>
{{#names}}
    <strong>{{name}}</strong>
{{/names}}
应用 {{= =}} 自定义定界符

有时候咱们须要扭转默认的定界符 {{}},那么就能够应用 {{= =}} 的形式自定义定界符。

例如:

{{=<% %>=}}

定界符被定义为了 <% %>,这样原先 {{key}} 的应用形式就变成了 <%key%>
再应用:

<%={{}}=%>

就从新把定界符改回了 {{}}

更多语法详情请查阅官网文档 mustache language。

结语

应用 search template 能够对搜寻进行无效的解耦,即应用程序只须要关注搜寻参数与返回后果,而不必关注具体应用的 DSL 查问语句,到底应用哪种 DSL 则由搜寻模板进行独自治理。

退出移动版