当前位置: 首页 > news >正文

elasticsearch的使用(二)

DSL查询

Elasticsearch的查询可以分为两大类:

  • 叶子查询(Leaf query clauses):一般是在特定的字段里查询特定值,属于简单查询,很少单独使用。

  • 复合查询(Compound query clauses):以逻辑方式组合多个叶子查询或者更改叶子查询的行为方式

语法示例:

GET /{索引库名}/_search
{"query": {"查询类型": {// .. 查询条件}}
}

无条件查询的类型是:match_all:

GET /items/_search
{"query": {"match_all": {}}
}

获取到的结果

你会发现虽然是match_all,但是响应结果中并不会包含索引库中的所有文档,而是仅有10条。这是因为处于安全考虑,elasticsearch设置了默认的查询页数。 

叶子查询

叶子查询的类型也可以做进一步细分,详情大家可以查看官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.12/query-dsl.html

这里列举一些常见的,例如:

  • 全文检索查询(Full Text Queries):利用分词器对用户输入搜索条件先分词,得到词条,然后再利用倒排索引搜索词条。例如:

    • match

    • multi_match

  • 精确查询(Term-level queries):不对用户输入搜索条件分词,根据字段内容精确值匹配。但只能查找keyword、数值、日期、boolean类型的字段。例如:

    • ids

    • term

    • range

  • 地理坐标查询:用于搜索地理位置,搜索方式很多,例如:

    • geo_bounding_box:按矩形搜索

    • geo_distance:按点和半径搜索

全文检索查询

全文检索的种类也很多,详情可以参考官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.12/full-text-queries.html

match(匹配单个字段),语法如下:

GET /{索引库名}/_search
{"query": {"match": {"字段名": "搜索条件"}}
}

使用实例:

GET /items/_search
{"query": {"match": {"name": "手机"}}
}

查询结果

multi_match(匹配多个字段):语法如下

GET /{索引库名}/_search
{"query": {"multi_match": {"query": "搜索条件","fields": ["字段1", "字段2"]}}
}

实例,我们搜索带华为二字的品牌与名字的商品 

GET /items/_search
{"query": {"multi_match": {"query": "华为","fields": ["brand","name"]}}
}

精确查询

推荐查找keyword、数值、日期、boolean类型的字段。

详情可以查看官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.12/term-level-queries.html

term查询(精确查询某个字段的相同值的文档),语法如下:

GET /{索引库名}/_search
{"query": {"term": {"字段名": {"value": "搜索条件"}}}
}

 实例,查询一个品牌为诺基亚的商品

GET /items/_search
{"query": {"term": {"brand": {"value": "诺基亚"}}}
}

range查询(处于某个字段范围内的文档),语法如下: 

GET /{索引库名}/_search
{"query": {"range": {"字段名": {"gte": {最小值},"lte": {最大值}}}}
}

 实例,查询价格处于500-1000的商品(由于数据库的存储单位为分,所以我们查询时多加两个零):

GET /items/_search
{"query": {"range": {"price": {"gte": 50000,"lte": 100000}}}
}

range是范围查询,对于范围筛选的关键字有:

  • gte:大于等于

  • gt:大于

  • lte:小于等于

  • lt:小于

复合查询

算分函数查询

采用的相关性打分算法是BM25算法,公式如下:

基本语法

function score 查询中包含四部分内容:

  • 原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)

  • 过滤条件:filter部分,符合该条件的文档才会重新算分

  • 算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数

    • weight:函数结果是常量

    • field_value_factor:以文档中的某个字段值作为函数结果

    • random_score:以随机数作为函数结果

    • script_score:自定义算分函数算法

  • 运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:

    • multiply:相乘

    • replace:用function score替换query score

    • 其它,例如:sum、avg、max、min

function score的运行流程如下:

  • 1)根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)

  • 2)根据过滤条件,过滤文档

  • 3)符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)

  • 4)将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。

因此,其中的关键点是:

  • 过滤条件:决定哪些文档的算分被修改

  • 算分函数:决定函数算分的算法

  • 运算模式:决定最终算分结果

实例,给IPhone这个品牌的手机算分提高十倍,分析如下:

  • 过滤条件:品牌必须为IPhone

  • 算分函数:常量weight,值为10

  • 算分模式:相乘multiply

GET /hotel/_search
{"query": {"function_score": {"query": {  .... }, // 原始查询,可以是任意条件"functions": [ // 算分函数{"filter": { // 满足的条件,品牌必须是Iphone"term": {"brand": "Iphone"}},"weight": 10 // 算分权重为2}],"boost_mode": "multipy" // 加权模式,求乘积}}
}

bool查询

bool查询,即布尔查询。就是利用逻辑运算来组合一个或多个查询子句的组合。bool查询支持的逻辑运算有:

  • must:必须匹配每个子查询,类似“与”

  • should:选择性匹配子查询,类似“或”

  • must_not:必须不匹配,不参与算分,类似“非”

  • filter:必须匹配,不参与算分

bool查询基本语法:

GET /items/_search
{"query": {"bool": {"must": [{"match": {"name": "手机"}}],"should": [{"term": {"brand": { "value": "vivo" }}},{"term": {"brand": { "value": "小米" }}}],"must_not": [{"range": {"price": {"gte": 2500}}}],"filter": [{"range": {"price": {"lte": 1000}}}]}}
}

出于性能考虑,与搜索关键字无关的查询尽量采用must_not或filter逻辑运算,避免参与相关性算分

 我们要搜索手机,但品牌必须是华为,价格必须是900~1599,那么可以这样写:

GET /items/_search
{"query": {"bool": {"must": [{"match": {"name": "手机"}}],"filter": [{"term": {"brand": { "value": "华为" }}},{"range": {"price": {"gte": 90000, "lt": 159900}}}]}}
}

排序

elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。不过分词字段无法排序,能参与排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。

详细说明可以参考官方文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.12/sort-search-results.html

 语法如下:

GET /indexName/_search
{"query": {"match_all": {}},"sort": [{"排序字段": {"order": "排序方式asc和desc"}}]
}

实例,按照商品价格进行排序

GET /items/_search
{"query": {"match_all": {}},"sort": [{"price": {"order": "desc"}}]
}

可以看到商品价格呈现降序 

分页

elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。

基础分页

elasticsearch中通过修改fromsize参数来控制要返回的分页结果:

  • from:从第几个文档开始

  • size:总共查询几个文档

类似于mysql中的limit ?, ?

官方文档如下:

https://www.elastic.co/guide/en/elasticsearch/reference/7.12/paginate-search-results.html

基本语法:

GET /items/_search
{"query": {"match_all": {}},"from": 0, // 分页开始的位置,默认为0"size": 10,  // 每页文档数量,默认10"sort": [{"price": {"order": "desc"}}]
}
深度分页

针对深度分页,elasticsearch提供了两种解决方案:

  • search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。

  • scroll:原理将排序后的文档id形成快照,保存下来,基于快照做分页。官方已经不推荐使用。

详情见文档:

https://www.elastic.co/guide/en/elasticsearch/reference/7.12/paginate-search-results.html

总结:

大多数情况下,我们采用普通分页就可以了。查看百度、京东等网站,会发现其分页都有限制。例如百度最多支持77页,每页不足20条。京东最多100页,每页最多60条。

因此,一般我们采用限制分页深度的方式即可,无需实现深度分页。

 高亮

  • 高亮词条都被加了<em>标签

  • <em>标签都添加了红色样式

高亮标签肯定是由服务端提供数据的时候已经加上的

基本语法如下:

GET /{索引库名}/_search
{"query": {"match": {"搜索字段": "搜索关键字"}},"highlight": {"fields": {"高亮字段名称": {"pre_tags": "<em>","post_tags": "</em>"}}}
}

 注意

  • 搜索必须有查询条件,而且是全文检索类型的查询条件,例如match

  • 参与高亮的字段必须是text类型的字段

  • 默认情况下参与高亮的字段要与搜索字段一致,除非添加:required_field_match=false

实例,我们将华为二字高亮显示

GET /items/_search
{"query": {"match": {"name": "华为"}},"highlight": {"fields": {"name": {"pre_tags": "<em>","post_tags": "</em>"}}}
}

总结:

查询的DSL是一个大的JSON对象,包含下列属性:

  • query:查询条件

  • fromsize:分页条件

  • sort:排序条件

  • highlight:高亮条件

数据聚合

Bucket聚合

其实就是以分类(category)字段对数据分组。category值一样的放在同一组,属于Bucket聚合中的Term聚合。

基本语法如下:

GET /items/_search
{"size": 0, "aggs": {"category_agg": {"terms": {"field": "category","size": 20}}}
}

语法说明:

  • size:设置size为0,就是每页查0条,则结果中就不包含文档,只包含聚合

  • aggs:定义聚合

    • category_agg:聚合名称,自定义,但不能重复

      • terms:聚合的类型,按分类聚合,所以用term

        • field:参与聚合的字段名称

        • size:希望返回的聚合结果的最大数量 

带条件聚合

我们需要从需求中分析出搜索查询的条件和聚合的目标:

  • 搜索查询条件:

    • 价格高于3000

    • 必须是手机

  • 聚合目标:统计的是品牌,肯定是对brand字段做term聚合

语法如下:

GET /items/_search
{"query": {"bool": {"filter": [{"term": {"category": "手机"}},{"range": {"price": {"gte": 300000}}}]}}, "size": 0, "aggs": {"brand_agg": {"terms": {"field": "brand","size": 20}}}
}

Metric聚合

语法如下:

GET /items/_search
{"query": {"bool": {"filter": [{"term": {"category": "手机"}},{"range": {"price": {"gte": 300000}}}]}}, "size": 0, "aggs": {"brand_agg": {"terms": {"field": "brand","size": 20},"aggs": {"stats_meric": {"stats": {"field": "price"}}}}}
}

可以看到我们在brand_agg聚合的内部,我们新加了一个aggs参数。这个聚合就是brand_agg的子聚合,会对brand_agg形成的每个桶中的文档分别统计。

  • stats_meric:聚合名称

    • stats:聚合类型,stats是metric聚合的一种

      • field:聚合字段,这里选择price,统计价格

由于stats是对brand_agg形成的每个品牌桶内文档分别做统计,因此每个品牌都会统计出自己的价格最小、最大、平均值。

总结

aggs代表聚合,与query同级,此时query的作用是?

  • 限定聚合的的文档范围

聚合必须的三要素:

  • 聚合名称

  • 聚合类型

  • 聚合字段

聚合可配置属性有:

  • size:指定聚合结果数量

  • order:指定聚合结果排序方式

  • field:指定聚合字段

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • MYSQL知识点(持续更新)
  • 详解Xilinx FPGA高速串行收发器GTX/GTP(4)--TX/RX接口的数据位宽和时钟设计
  • 【Python机器学习】支持向量机——在复杂数据上应用核函数
  • 27集28集 ESP32 AIchat cmake编译解密-《MCU嵌入式AI开发笔记》
  • 谷粒商城实战笔记-nginx问题记录
  • 服务器测试之RAID知识梳理
  • MySQL的三大关键日志:Bin Log、Redo Log与Undo Log
  • 【开端】JAVA Mono<Void>向前端返回没有登陆或登录超时 暂无权限访问信息组装
  • Zookeeper的监听机制及原理解析
  • 算法【前缀和与差分】
  • LeNet5模型搭建
  • 华为OD-D卷小明找位置
  • 学习记录(9):Prompt提示词技巧
  • source insight 3.5快捷键合集
  • 模板方法模式(Template Method Pattern)
  • 分享一款快速APP功能测试工具
  • [Vue CLI 3] 配置解析之 css.extract
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 「译」Node.js Streams 基础
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • Angular4 模板式表单用法以及验证
  • canvas 高仿 Apple Watch 表盘
  • css系列之关于字体的事
  • egg(89)--egg之redis的发布和订阅
  • iOS | NSProxy
  • JavaScript函数式编程(一)
  • Linux下的乱码问题
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Nodejs和JavaWeb协助开发
  • PhantomJS 安装
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • PHP那些事儿
  • Python实现BT种子转化为磁力链接【实战】
  • Redis 懒删除(lazy free)简史
  • SpiderData 2019年2月16日 DApp数据排行榜
  • SQLServer之索引简介
  • webpack入门学习手记(二)
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 分享一份非常强势的Android面试题
  • 我有几个粽子,和一个故事
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • 翻译 | The Principles of OOD 面向对象设计原则
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • # Redis 入门到精通(一)数据类型(4)
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #{} 和 ${}区别
  • #if 1...#endif
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • (21)起落架/可伸缩相机支架
  • (26)4.7 字符函数和字符串函数
  • (C语言)二分查找 超详细
  • (LLM) 很笨
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (几何:六边形面积)编写程序,提示用户输入六边形的边长,然后显示它的面积。