Elasticsearch:search template


我們發現一些用戶經常編寫了一些非常冗長和復雜的查詢 - 在很多情況下,相同的查詢會一遍又一遍地執行,但是會有一些不同的值作為參數來查詢。在這種情況下,我們覺得使用一個search template(搜索模板)來做這樣的工作非常合適。搜索模板允許您使用可在執行時定義的參數定義查詢。

Search template的好處是:

  • 避免在多個地方重復代碼
  • 更容易測試和執行您的查詢
  • 在應用程序間共享查詢
  • 允許用戶只執行一些預定義的查詢
  • 將搜索邏輯與應用程序邏輯分離

定義一個Search template

首先,我們來定義一個search template來看看它到底是什么東西。使用_scripts端點將模板存儲在集群狀態中。在search template中使用的語言叫做mustache。(http://mustache.github.io/mustache.5.html)

    POST _scripts/my_search_template
    {
      "script": {
        "lang": "mustache",
        "source": {
          "query": {
            "match": {
              "{{my_field}}": "{{my_value}}"
            }
          }
        }
      }
    }

在這里,我們定義了一個叫做my_search_template的search template。如果我們想更新這個search template,我們可以直接進行修改,然后再次運行上面的命令即可。

在match的字段里,我們定義了兩個參數:my_field及my_value。下面,我們來首先建立一個叫做twitter的數據庫:

    PUT twitter/_doc/1
    {
      "user" : "雙榆樹-張三",
      "message" : "今兒天氣不錯啊,出去轉轉去",
      "uid" : 2,
      "age" : 20,
      "city" : "北京",
      "province" : "北京",
      "country" : "中國",
      "address" : "中國北京市海淀區",
      "location" : {
        "lat" : "39.970718",
        "lon" : "116.325747"
      }
    }
     
    PUT twitter/_doc/2
    {
      "user" : "虹橋-老吳",
      "message" : "好友來了都今天我生日,好友來了,什么 birthday happy 就成!",
      "uid" : 7,
      "age" : 90,
      "city" : "上海",
      "province" : "上海",
      "country" : "中國",
      "address" : "中國上海市閔行區",
      "location" : {
        "lat" : "31.175927",
        "lon" : "121.383328"
      }
    }

我們這里把上面的兩個文檔存於到twitter的index之中。我們現在可以使用我們剛才定義的search template來進行搜索:

    GET twitter/_search/template
    {
      "id": "my_search_template",
      "params": {
        "my_field": "city",
        "my_value": "北京"
      }
    }

顯示的結果是:

    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 0.9808292,
        "hits" : [
          {
            "_index" : "twitter",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 0.9808292,
            "_source" : {
              "user" : "雙榆樹-張三",
              "message" : "今兒天氣不錯啊,出去轉轉去",
              "uid" : 2,
              "age" : 20,
              "city" : "北京",
              "province" : "北京",
              "country" : "中國",
              "address" : "中國北京市海淀區",
              "location" : {
                "lat" : "39.970718",
                "lon" : "116.325747"
              }
            }
          }
        ]
      }
    }

顯示它只顯示了我們的city為北京的一個文檔,另外一個上海的文檔沒有做任何的顯示。說明我們定義的search template是工作的。

條件判斷

在Mustache語言中,它沒有if/else這樣的判斷,但是你可以定section來跳過它如果那個變量是false還是沒有被定義:

    {{#param1}}
        "This section is skipped if param1 is null or false"
    {{/param1}}

我們定義如下的一個search template:

    POST _scripts/docs_from_beijing_and_age
    {
      "script": {
        "lang": "mustache",
        "source": 
    """
        {
          "query": {
            "bool": {
              "must": [
                {
                  "match": {
                    "city": "{{search_term}}"
                  }
                }
                {{#search_age}}
                ,
                {
                  "range": {
                    "age": {
                      "gte": {{search_age}}
                    }
                  }
                }
                {{/search_age}}
              ]
            }
          }
        }
    """
      }
    }

在這里,我們同時定義了兩個變量:search_term及search_age。針對search_age,我們做了一個判斷,如果它有定義,及做一個range的查詢。如果沒有定義,就只用search_term。那么我們來做如下的實驗:

    GET twitter/_search/template
    {
      "id": "docs_from_beijing_and_age",
      "params": {
        "search_term": "北京"
      }
    }

顯示的結果是:

    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 0.9808292,
        "hits" : [
          {
            "_index" : "twitter",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 0.9808292,
            "_source" : {
              "user" : "雙榆樹-張三",
              "message" : "今兒天氣不錯啊,出去轉轉去",
              "uid" : 2,
              "age" : 20,
              "city" : "北京",
              "province" : "北京",
              "country" : "中國",
              "address" : "中國北京市海淀區",
              "location" : {
                "lat" : "39.970718",
                "lon" : "116.325747"
              }
            }
          }
        ]
      }
    }

顯然,city為北京的文檔已經被搜索到了。如果我們做如下的查詢:

    GET twitter/_search/template
    {
      "id": "docs_from_beijing_and_age",
      "params": {
        "search_term": "北京",
        "search_age": "30"
      }
    }

我們將搜索不到任何的結果,這是因為在這次查詢中search_age已經被啟用,而且在數據庫中沒有一個文檔是來自“北京”,並且年齡大於30的。我們可以做如下的查詢:

    GET twitter/_search/template
    {
      "id": "docs_from_beijing_and_age",
      "params": {
        "search_term": "北京",
        "search_age": "20"
      }
    }

那么這次的顯示結果為:

    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 1.9808292,
        "hits" : [
          {
            "_index" : "twitter",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 1.9808292,
            "_source" : {
              "user" : "雙榆樹-張三",
              "message" : "今兒天氣不錯啊,出去轉轉去",
              "uid" : 2,
              "age" : 20,
              "city" : "北京",
              "province" : "北京",
              "country" : "中國",
              "address" : "中國北京市海淀區",
              "location" : {
                "lat" : "39.970718",
                "lon" : "116.325747"
              }
            }
          }
        ]
      }
    }

顯然這次我們搜索到我們想要的結果。

查詢search template

GET _scripts/<templateid>

針對我們的情況:

GET _scripts/docs_from_beijing_and_age

顯示的結果為:

    {
      "_id" : "docs_from_beijing_and_age",
      "found" : true,
      "script" : {
        "lang" : "mustache",
        "source" : """
        {
          "query": {
            "bool": {
              "must": [
                {
                  "match": {
                    "city": "{{search_term}}"
                  }
                }
                {{#search_age}}
                ,
                {
                  "range": {
                    "age": {
                      "gte": {{search_age}}
                    }
                  }
                }
                {{/search_age}}
              ]
            }
          }
        }
    """
      }
    }

這個正是我們之前定義的一個search template。

刪除一個search template

我們可以通過如下的命令來刪除一個已經創建的search template:

DELETE _scripts/<templateid>

驗證search template

我們可以通過_render端點來驗證我們的search template。比如:

    GET _render/template
    {
      "source": """
        {
          "query": {
            "bool": {
              "must": [
                {
                  "match": {
                    "city": "{{search_term}}"
                  }
                }
                {{#search_age}}
                ,
                {
                  "range": {
                    "age": {
                      "gte": {{search_age}}
                    }
                  }
                }
                {{/search_age}}
              ]
            }
          }
        }
    """,
      "params": {
        "search_term": "北京",
        "search_age": "20"
      }
    }

那么顯示的結果是:

    {
      "template_output" : {
        "query" : {
          "bool" : {
            "must" : [
              {
                "match" : {
                  "city" : "北京"
                }
              },
              {
                "range" : {
                  "age" : {
                    "gte" : 20
                  }
                }
              }
            ]
          }
        }
      }
    }

顯然,這個就是我們想要的結果。

參考:
【1】https://www.elastic.co/guide/en/elasticsearch/reference/7.4/search-template.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM