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

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 1、线上实战提问Elasticsearch做模版查询的时候,在使用 terms 进行批量查询的时候放入数组在模版中进行查询失败,类似于模版传入数组该如何实现?问题来源:死磕Elasticsearch知识星球

image.png

链接

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


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


# 定义索引

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}

查询语句:


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"

             }

           }

         }

       }

     }

   }

 }

}

查询模版参数:


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 搜索模板举例

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


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有错吗?

实战一把,报错如下:


{

 "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,如下:


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 检索模板。


GET _search/template

{

 "source": "{ \"query\": { \"terms\": {{#toJson}}statuses{{/toJson}} }}",

 "params": {

   "statuses" : {

       "itemid": [ 1, 2 ]

   }

 }

}

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


错误1:source 里面的内容要加:"\" 。


错误2:查询模版参数中的 statuses 和 itemid 位置写错了。


官方文档的说法:


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


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


第二步:将第一步内容转成script 形式。


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++,全局替换。

image.png

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


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


实战问题答案


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


推荐:


重磅 | 死磕 Elasticsearch 方法论认知清单(2020年国庆更新版)


能拿驾照就能通过 Elastic 认证考试!

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
4月前
|
SQL JSON 大数据
ElasticSearch的简单介绍与使用【进阶检索】 实时搜索 | 分布式搜索 | 全文搜索 | 大数据处理 | 搜索过滤 | 搜索排序
这篇文章是Elasticsearch的进阶使用指南,涵盖了Search API的两种检索方式、Query DSL的基本语法和多种查询示例,包括全文检索、短语匹配、多字段匹配、复合查询、结果过滤、聚合操作以及Mapping的概念和操作,还讨论了Elasticsearch 7.x和8.x版本中type概念的变更和数据迁移的方法。
ElasticSearch的简单介绍与使用【进阶检索】 实时搜索 | 分布式搜索 | 全文搜索 | 大数据处理 | 搜索过滤 | 搜索排序
|
4月前
|
存储 API 数据库
检索服务elasticsearch索引(Index)
【8月更文挑战第23天】
70 6
|
4月前
|
存储 负载均衡 监控
检索服务elasticsearch节点(Node)
【8月更文挑战第23天】
62 5
|
4月前
|
存储 监控 负载均衡
检索服务elasticsearch集群(Cluster)
【8月更文挑战第23天】
67 3
|
4月前
|
存储 监控 负载均衡
检索服务elasticsearch分布式结构
【8月更文挑战第22天】
49 3
|
1月前
|
测试技术 API 开发工具
ElasticSearch7.6.x 模板及滚动索引创建及注意事项
ElasticSearch7.6.x 模板及滚动索引创建及注意事项
44 8
|
4月前
|
网络协议 Java API
SpringBoot整合Elasticsearch-Rest-Client、测试保存、复杂检索
这篇文章介绍了如何在SpringBoot中整合Elasticsearch-Rest-Client,并提供了保存数据和进行复杂检索的测试示例。
SpringBoot整合Elasticsearch-Rest-Client、测试保存、复杂检索
|
3月前
|
存储 自然语言处理 关系型数据库
ElasticSearch基础3——聚合、补全、集群。黑马旅游检索高亮+自定义分词器+自动补全+前后端消息同步
聚合、补全、RabbitMQ消息同步、集群、脑裂问题、集群分布式存储、黑马旅游实现过滤和搜索补全功能
ElasticSearch基础3——聚合、补全、集群。黑马旅游检索高亮+自定义分词器+自动补全+前后端消息同步
|
4月前
|
SQL 存储 自然语言处理
检索服务elasticsearch全文搜索
【8月更文挑战第22天】
61 3
|
4月前
|
SQL 存储 监控
检索服务elasticsearch
【8月更文挑战第21天】
41 0