前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一步步拆解解决 Elasticsearch 检索模板问题

一步步拆解解决 Elasticsearch 检索模板问题

作者头像
DevOps云学堂
发布2020-11-10 10:07:36
5090
发布2020-11-10 10:07:36
举报
文章被收录于专栏:DevOps持续集成DevOps持续集成

1、线上实战提问

Elasticsearch做模版查询的时候,在使用 terms 进行批量查询的时候放入数组在模版中进行查询失败,类似于模版传入数组该如何实现?

问题来源:死磕Elasticsearch知识星球

代码语言:javascript
复制
# 定义索引
PUT uint-2020-08-17
{
    "mappings": {
      "properties": {
        "clock": {
          "type": "date",
          "format": "epoch_second"
        },
        "itemid": {
          "type": "long"
        },
        "ns": {
          "type": "long"
        },
        "ttl": {
          "type": "long"
        },
        "value": {
          "type": "long"
        }
      }
    }
}

# 添加内容
PUT uint-2020-08-17/_bulk
{ "index" : {  "_id" : "1" } }
{"itemid":1,"ns":643214179,"clock":1597752311,"value":"1123","ttl":604800}
{ "index" : {  "_id" : "2" } }
{"itemid":2,"ns":643214179,"clock":1597752311,"value":"123555","ttl":604800}
{ "index" : {  "_id" : "3" } }
{"itemid":3,"ns":643214179,"clock":1597752311,"value":"1","ttl":604800}
{ "index" : {  "_id" : "4" } }
{"itemid":4,"ns":643214179,"clock":1597752311,"value":"134","ttl":604800}
{ "index" : {  "_id" : "5" } }
{"itemid":2,"ns":643214179,"clock":1597752311,"value":"123556","ttl":604800}

查询语句:

代码语言:javascript
复制
PUT _scripts/item_agg
{
  "script": {
    "lang": "mustache",
    "source": {
      "_source": [
        "value"
      ],
      "size": 0,
      "query": {
        "bool": {
          "filter": [
            {
              "terms": "{{#toJson}}statuses{{/toJson}}"
            },
            {
              "range": {
                "clock": {
                  "gte": "{{startTime}}",
                  "lte": "{{endTime}}"
                }
              }
            }
          ]
        }
      },
      "aggs": {
        "group_terms": {
          "terms": {
            "field": "itemid"
          },
          "aggs": {
            "avg_value": {
              "avg": {
                "field": "value"
              }
            },
            "max_value": {
              "max": {
                "field": "value"
              }
            }
          }
        }
      }
    }
  }
}

查询模版参数:

代码语言:javascript
复制
POST uint-*/_search/template
{
  "id": "item_agg",
  "params": {
    "itemid":{
      "statuses":[1,2]
    },
    "startTime":1597752309,
    "endTime":1597752333
    
  }
}

以上内容看着很长,根据注释拆解为:

  • 定义索引、
  • 插入数据、
  • 创建模板、
  • 构造参数检索

四个子部分你就不会恐慌了。

2、知识点解读——搜索模板

2.1 什么是搜索模板?

很多人都听说使用过 索引模板 index template,索引模板的好处:

  • 便于跨索引统一建模;
  • 尤其适合数据量巨大、索引字段类似的业务系统;
  • 灵活便捷。

检索模板(search template)大家使用相对较少,在实战业务场景中:每次业务请求都要构造 DSL,比如:这次查title、下次查content,除此之外的 DSL 部分 都一样,但两次请求:后端代码那里就要有相应的修改和适配。有没有不修改、拼接DSL使用检索的方案?这就引出了搜索模板。

搜索模板与关系数据库中的存储过程非常相似。可以将常用查询定义为模板,并且使用 Elasticsearch 的应用程序可以简单地通过其 ID 引用查询。

模板接受在运行时指定参数。搜索模板存储在服务器端,可以在不更改客户端代码的情况下进行修改。

模板使用Mustache模板引擎表示。关于 Mustache 可以访问:

http://mustache.github.io/mustache.5.html。

2.2 搜索模板举例

根据第一部分实战中的数据,定义了如下的模板。

代码语言:javascript
复制
PUT _scripts/cur_search_template
{
  "script": {
    "lang": "mustache",
    "source": {
      "query": {
      "match": {
        "{{cur_field}}": "{{cur_value}}"
      }
    },
    "size": "{{cur_size}}"
    }
  }
}

POST uint-*/_search/template
{
  "id": "cur_search_template",
  "params": {
    "cur_field":"itemid",
    "cur_value":1,
    "cur_size":50
    
  }
}

该模板:支持用户自定义动态设置搜索字段及搜索参数字段。

实战中可以通过如下_scripts 的方式,将检索模板定义到服务器端。

如果想检索别的字段:客户端或者请求端传递不同的参数即可。

真正意义上的实现了:检索和请求参数的分离。

更多原理和基础参见官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html

2.3 search template 的语法很让人头脑

以下内容摘自:Wood 大叔的——Elastic认证考试心得。

按照要求写一个search template 熟悉search template的mustache模版语言即可轻松写出,但是很遗憾,平常没用过search template,虽然知道个大概,但是当时写的时候,不知道哪里语法有问题,PUT template总是不成功。猜想可能是哪个位置的字符没有转译产生非法json字符,或者哪一层嵌套有问题。总之就是调试不成功,又浪费了很多时间。 https://elasticsearch.cn/article/6133

如上引用想说明的是:search template的语法比较复杂,如果没用过,很容易头大。

3、问题拆解

3.1 原有DSL有错吗?

实战一把,报错如下:

代码语言:javascript
复制
{
  "error": {
    "root_cause": [
      {
        "type": "parsing_exception",
        "reason": "[terms] query malformed, no start_object after query name",
        "line": 1,
        "col": 67
      }
    ],
    "type": "parsing_exception",
    "reason": "[terms] query malformed, no start_object after query name",
    "line": 1,
    "col": 67
  },
  "status": 400
}

3.2 哪里出了问题?

拆解一下。

  • script 部分无非包括:检索部分和聚合部分。
  • 检索部分是定义 search template 的核心,聚合部分无需关注。

这个时候,可以写一个检索 DSL验证一下是否ok,如下:

代码语言:javascript
复制
POST /_search
{
  "_source": [
    "value"
  ],
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {
          "terms": {
            "itemid": [
              1,
              2
            ]
          }
        },
        {
          "range": {
            "clock": {
              "gte": 1597752309,
              "lte": 1597752333
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "group_terms": {
      "terms": {
        "field": "itemid"
      },
      "aggs": {
        "avg_value": {
          "avg": {
            "field": "value"
          }
        },
        "max_value": {
          "max": {
            "field": "value"
          }
        }
      }
    }
  }
}
  • 而检索和聚合都没错,那多半就是定义 search template 部分出错了。

问题就这么一点点拆解了。

上来直接改这个 DSL貌似也无从下手,那咱们就做:最小化处理吧。

抛去所有:_source、size、aggs、range query 部分,只保留 terms 脚本应该怎么正确的写?

来吧,实战一把:

  • 第一步:最小化 terms 检索模板。
代码语言:javascript
复制
GET _search/template
{
  "source": "{ \"query\": { \"terms\": {{#toJson}}statuses{{/toJson}} }}",
  "params": {
    "statuses" : {
        "itemid": [ 1, 2 ]
    }
  }
}

用现在正确的对比第一部分出错的,可以找到如下两处错误:

  • 错误1:source 里面的内容要加:"\" 。
  • 错误2:查询模版参数中的 statuses 和 itemid 位置写错了。

官方文档的说法:

The {{#toJson}}parameter{{/toJson}} function can be used to convert parameters like maps and array to their JSON representation:

statuses 就是个辅助参数,我们核心的参数是 itemid。

  • 第二步:将第一步内容转成script 形式。
代码语言:javascript
复制
POST _scripts/test_script_01
{
  "script": {
    "lang": "mustache",
    "source": "{ \"query\": { \"terms\": {{#toJson}}statuses{{/toJson}} }}"
  }
}

POST uint-*/_search/template 
{
  "id": "test_script_01",
  "params": {
    "statuses": {
      "itemid": [
        1,
        2
      ]
    },
    "startTime": 1597752309,
    "endTime": 1597752333
  }
}
  • 第三步:按照实战要求补全参数即可。

注意补全的时候,我建议:拷贝 DSL(格式化一行的版本)到第三方文本工具如:Nodepad++,全局替换。

切记不要手敲,很容易出错。

替换到模板的 source 部分,然后再根据第一步、第二步内容修改即可。

  • 实战问题答案
代码语言:javascript
复制
GET _search/template
{
  "source": "{\"_source\":[\"value\"],\"size\":0,\"query\":{\"bool\":{\"filter\":[{\"terms\":{{#toJson}}statuses{{/toJson}}},{\"range\":{\"clock\":{\"gte\":{{startTime}},\"lte\":{{endTime}}}}}]}},\"aggs\":{\"group_terms\":{\"terms\":{\"field\":\"itemid\"},\"aggs\":{\"avg_value\":{\"avg\":{\"field\":\"value\"}},\"max_value\":{\"max\":{\"field\":\"value\"}}}}}}",
    "params": {
    "statuses" : {
        "itemid": [ 1, 2 ]
    },
       "startTime":1597752309,
    "endTime":1597752333
  }
}

拷贝 source 部分转换为脚本格式就可以,篇幅问题,不再赘述。

4、小结

看似复杂,拆解后便不复杂。

看似很难,拆解后就很简单。

检索模板用的好,前后端扯皮少、效率高很多!

你的小问题,我的大问题。

和你一起,死磕 Elasticsearch!

参考:

https://elastic-search-in-action.medcl.com/3.site_search/3.3.search_box/search_template/

https://subscription.packtpub.com/book/big_data_and_business_intelligence/9781787128453/7/ch07lvl1sec61/search-templates

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DevOps云学堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、线上实战提问
  • 2、知识点解读——搜索模板
    • 2.1 什么是搜索模板?
      • 2.2 搜索模板举例
        • 2.3 search template 的语法很让人头脑
        • 3、问题拆解
          • 3.1 原有DSL有错吗?
            • 3.2 哪里出了问题?
            • 4、小结
            相关产品与服务
            Elasticsearch Service
            腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档