ElasticSearch 集群架构与搜索深入理解

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
{

“query”: {

“match”: {

“remark”: {

“query”: “java developer”,

“operator”: “and”

}

}

}

}

复制代码

上述语法中,如果将operator的值改为or。则与第一个案例搜索语法效果一致。默认的ES执行搜索的时候,operator就是or。

如果在搜索的结果document中,需要remark字段中包含多个搜索词条中的一定比例,可以使用下述语法实现搜索。其中minimum_should_match可以使用百分比或固定数字。百分比代表query搜索条件中词条百分比,如果无法整除,向下匹配(如,query条件有3个单词,如果使用百分比提供精准度计算,那么是无法除尽的,如果需要至少匹配两个单词,则需要用67%来进行描述。如果使用66%描述,ES 则认为匹配一个单词即可。)。固定数字代表query搜索条件中的词条,至少需要 匹配多少个。

GET /es_db/_search

{

“query”: {

“match”: {

“remark”: {

“query”: “java architect assistant”,

“minimum_should_match”: “68%”

}

}

}

}

复制代码

如果使用should+bool搜索的话,也可以控制搜索条件的匹配度。具体如下:下述

案例代表搜索的document中的remark字段中,必须匹配java、developer、 assistant三个词条中的至少2个。

GET /es_db/_search

{ “query”: {

“bool”: {

“should”: [

{

“match”: {

“remark”: “java”

}

},

{

“match”: {

“remark”: “developer”

}

},

{

“match”: {

“remark”: “assistant”

}

}

],

“minimum_should_match”: 2

}

}

}

复制代码

5.2、match 的底层转换


其实在ES中,执行match搜索的时候,ES底层通常都会对搜索条件进行底层转换,

来实现最终的搜索结果。如:

GET /es_db/_search

{

“query”: {

“match”: {

“remark”: “java developer”

}

}

}

转换后是:

GET /es_db/_search

{

“query”: { “bool”: {

“should”: [

{

“term”: {

“remark”: “java”

}

},

{

“term”: {

“remark”: {

“value”: “developer”

}

}

}

]

}

}

}

完全匹配

GET /es_db/_search

{

“query”: {

“match”: {

“remark”: {

“query”: “java developer”,

“operator”: “and”

}

}

}

}

转换后是:

GET /es_db/_search

{

“query”: {

“bool”: {

“must”: [

{

“term”: {

“remark”: “java”

} },

{

“term”: {

“remark”: {

“value”: “developer”

}

}

}

]

}

}

}

匹配度

GET /es_db/_search

{

“query”: {

“match”: {

“remark”: {

“query”: “java architect assistant”,

“minimum_should_match”: “68%”

}

}

}

}

转换后为:

GET /es_db/_search

{

“query”: {

“bool”: {

“should”: [

{

“term”: {

“remark”: “java”

}

},

{

“term”: {

“remark”: “architect”

}

}, {

“term”: {

“remark”: “assistant”

}

}

],

“minimum_should_match”: 2

}

}

}

复制代码

**建议,如果不怕麻烦,尽量使用转换后的语法执行搜索,效率更高。 **

**如果开发周期短,工作量大,使用简化的写法。 **

5.3、boost权重控制


搜索document中remark字段中包含java的数据,如果remark中包含developer

或architect,则包含architect的document优先显示。(就是将architect数据匹

配时的相关度分数增加)。

一般用于搜索时相关度排序使用。如:电商中的综合排序。将一个商品的销

量,广告投放,评价值,库存,单价比较综合排序。在上述的排序元素中,广告投

放权重最高,库存权重最低。

GET /es_db/_search

{

“query”:{

“bool”:{

“must”:[

{

“match”:{

“remark”:“java”

}

}

],

“should”:[

{

“match”:{

“remark”:{

“query”:“developer”,

“boost”:1

}

}

},

{

“match”:{

“remark”:{

“query”:“architect”,

“boost”:3

}

}

}

]

}

}

}

复制代码

5.4、基于dis_max实现best fields策略进行多字段搜索


best fields策略: 搜索的document中的某一个field,尽可能多的匹配搜索条件。与之相反的是,尽可能多的字段匹配到搜索条件(most fields策略)。如百度搜索使用这种策略。

优点:精确匹配的数据可以尽可能的排列在最前端,

且可以通过 minimum_should_match来去除长尾数据,避免长尾数据字段对排序结果的影响。

长尾数据比如说我们搜索4个关键词,但很多文档只匹配1个,也显示出来了,这些文档其实不是我们想要的

_缺点:相对排序不均匀。 _

_dis_max语法: 直接获取搜索的多条件中的,单条件query相关度分数最高的数据,以这个数据做相关度排序。 _

下述的案例中,就是找name字段中rod匹配相关度分数或remark字段中java

developer匹配相关度分数,哪个高,就使用哪一个相关度分数进行结果排序。

GET /es_db/_search

{

“query”: {

“dis_max”: {

“queries”: [

{

“match”: { “name”: “rod”

}

},

{

“match”: {

“remark”: “java developer”

}

}

]

}

}

}

返回结果

#! Elasticsearch built-in security features are not enabled. Without authentication, your cluster could be accessible to anyone. See https://www.elastic.co/guide/en/elasticsearch/reference/7.15/security-minimal-setup.html to enable security.

{

“took” : 1,

“timed_out” : false,

“_shards” : {

“total” : 1,

“successful” : 1,

“skipped” : 0,

“failed” : 0

},

“hits” : {

“total” : {

“value” : 4,

“relation” : “eq”

},

“max_score” : 1.6375021,

“hits” : [

{

“_index” : “es_db”,

“_type” : “_doc”,

“_id” : “3”,

“_score” : 1.6375021,

“_source” : {

“name” : “rod”,

“sex” : 0,

“age” : 26,

“address” : “广州白云山公园”,

“remark” : “php developer”

}

},

{

“_index” : “es_db”,

“_type” : “_doc”,

“_id” : “1”,

“_score” : 1.4691012,

“_source” : {

“name” : “张三”,

“sex” : 1,

“age” : 25,

“address” : “广州天河公园”,

“remark” : “java developer”

}

},

{

“_index” : “es_db”,

“_type” : “_doc”,

“_id” : “2”,

“_score” : 0.5598161,

“_source” : {

“name” : “李四”,

“sex” : 1,

“age” : 28,

“address” : “广州荔湾大厦”,

“remark” : “java assistant”

}

},

{

“_index” : “es_db”,

“_type” : “_doc”,

“_id” : “5”,

“_score” : 0.46919835,

“_source” : {

“name” : “小明”,

“sex” : 0,

“age” : 19,

“address” : “长沙岳麓山”,

“remark” : “java architect assistant”

}

}

]

}

}

复制代码

5.5、基于tie_breaker参数优化dis_max搜索效果


dis_max是将多个搜索query条件中相关度分数最高的用于结果排序,忽略其他 query分数,在某些情况下,可能还需要其他query条件中的相关度介入最终的结果排序,这个时候可以使用tie_breaker参数来优化dis_max搜索。

tie_breaker参数代表的含义是:将其他query搜索条件的相关度分数乘以参数值,再参与到结果排序中。如果不定义此参数,相当于参数值为0。所以其他query条件的相关度分数被忽略。

GET /es_db/_search

{

“query”: {

“dis_max”: {

“queries”: [

{

“match”: {

“name”: “rod”

}

},

{

“match”: {

“remark”: “java developer”

}

}

],

“tie_breaker”:0.5

}

}

}

复制代码

5.6、使用multi_match简化dis_max+tie_breaker


ES中相同结果的搜索也可以使用不同的语法语句来实现。不需要特别关注,只要能够实现搜索,就是完成任务!

如:

GET /es_db/_search

{

“query”:{

“dis_max”:{

“queries”:[

{

“match”:{

“name”:“rod”

}

},

{

“match”:{

“remark”:{

“query”:“javadeveloper”,

“boost”:2,

“minimum_should_match”:2

}

}

}

],

“tie_breaker”:0.5

}

}

}

返回结果

#! Elasticsearch built-in security features are not enabled. Without authentication, your cluster could be accessible to anyone. See https://www.elastic.co/guide/en/elasticsearch/reference/7.15/security-minimal-setup.html to enable security.

{

“took” : 0,

“timed_out” : false,

“_shards” : {

“total” : 1,

“successful” : 1,

“skipped” : 0,

“failed” : 0

},

“hits” : {

“total” : {

“value” : 1,

“relation” : “eq”

},

“max_score” : 1.6375021,

“hits” : [

{

“_index” : “es_db”,

“_type” : “_doc”,

“_id” : “3”,

“_score” : 1.6375021,

“_source” : {

“name” : “rod”,

“sex” : 0,

“age” : 26,

“address” : “广州白云山公园”,

“remark” : “php developer”

}

}

]

}

}

#使用multi_match语法为:其中type常用的有best_fields和most_fields。^n代表权重,相当于"boost":n。

GET /es_db/_search

{

“query”:{

“multi_match”:{

“query”: “rod java developer”,

“fields”: [“name”,“remark^2”],

“type”: “best_fields”,

“tie_breaker”: 0.5,

“minimum_should_match”: “50%”

}

}

}

复制代码

5.7、cross fields搜索


cross fields : 一个唯一的标识,分部在多个fields中,使用这种唯一标识

搜索数据就称为cross fields搜索。如:人名可以分为姓和名,地址可以分为省、

市、区县、街道等。那么使用人名或地址来搜索document,就称为cross fields搜

索。

实现这种搜索,一般都是使用most fields搜索策略。因为这就不是一个field

的问题。

**Cross fields搜索策略,是从多个字段中搜索条件数据。默认情况下,和most **

**fields搜索的逻辑是一致的,计算相关度分数是和best fields策略一致的。一般 **

**来说,如果使用cross fields搜索策略,那么都会携带一个额外的参数operator。 **

**用来标记搜索条件如何在多个字段中匹配。 **

当然,在ES中也有cross fields搜索策略。具体语法如下:

GET /es_db/_search

{

“query”: {

“multi_match”: {

“query”: “java developer”,

“fields”: [“name”, “remark”],

“type”: “cross_fields”,

“operator” : “and”

}

}

}

复制代码

上述语法代表的是,搜索条件中的java必须在name或remark字段中匹配,

developer也必须在name或remark字段中匹配。most field策略问题:most fields策略是尽可能匹配更多的字段,所以会导致

精确搜索结果排序问题。又因为cross fields搜索,不能使用

minimum_should_match来去除长尾数据。

所以在使用most fields和cross fields策略搜索数据的时候,都有不同的缺

陷。所以商业项目开发中,都推荐使用best fields策略实现搜索。

5.8、copy_to组合fields


京东中,如果在搜索框中输入“手机”,点击搜索,那么是在商品的类型名称、商品的名称、商品的卖点、商品的描述等字段中,哪一个字段内进行数据的匹配?如果使用某一个字段做搜索不合适,那么使用_all做搜索是否合适?也不合适,因为_all字段中可能包含图片,价格等字段。假设,有一个字段,其中的内容包括(但不限于):商品类型名称、商品名称、 商品卖点等字段的数据内容。是否可以在这个特殊的字段上进行数据搜索匹配?

{

“category_name” : “手机”,

“product_name” : “一加6T手机”,

“price” : 568800,

“sell_point” : “国产最好的Android手机”,

“tags”: [“8G+128G”, “256G可扩展”],

“color” : “红色”,

“keyword” : “手机 一加6T手机 国产最好的Android手机”

}

复制代码

copy_to : 就是将多个字段,复制到一个字段中,实现一个多字段组合。copy_to

可以解决cross fields搜索问题,在商业项目中,也用于解决搜索条件默认字段问

题。

如果需要使用copy_to语法,则需要在定义index的时候,手工指定mapping映射策

略。

copy_to语法:

PUT /es_db/_mapping

{

“properties”: {

“provice” : {

“type”: “text”,

“analyzer”: “standard”,

“copy_to”: “address”

},

“city” : {

“type”: “text”,

“analyzer”: “standard”,

“copy_to”: “address”

},

“street” : {

“type”: “text”,

“analyzer”: “standard”,

“copy_to”: “address”

},

“address” : {

“type”: “text”,

“analyzer”: “standard”

}

}

}

复制代码

上述的mapping定义中,是新增了4个字段,分别是provice、city、street、

address,其中provice、city、street三个字段的值,会自动复制到address字段

中,实现一个字段的组合。那么在搜索地址的时候,就可以在address字段中做条

件匹配,从而避免most fields策略导致的问题。在维护数据的时候,不需对

address字段特殊的维护。因为address字段是一个组合字段,是由ES自动维护的。

类似java代码中的推导属性。在存储的时候,未必存在,但是在逻辑上是一定存在

的,因为address是由3个物理存在的属性province、city、street组成的。

5.9、近似匹配


前文都是精确匹配。如doc中有数据java assistant,那么搜索jave是搜索不到

数据的。因为jave单词在doc中是不存在的。

如果搜索的语法是:

GET _search

{

“query” : {

“match” : {

“name” : “jave”

}

}

}

复制代码

如果需要的结果是有特殊要求,如:hello world必须是一个完整的短语,不

可分割;或document中的field内,包含的hello和world单词,且两个单词之间离

的越近,相关度分数越高。那么这种特殊要求的搜索就是近似搜索。包括hell搜索

条件在hello world数据中搜索,包括h搜索提示等都数据近似搜索的一部分。

如何上述特殊要求的搜索,使用match搜索语法就无法实现了。

5.10、match phrase


短语搜索。**就是搜索条件不分词。代表搜索条件不可分割。 **

如果hello world是一个不可分割的短语,我们可以使用前文学过的短语搜索

match phrase来实现。语法如下:

GET _search

{

“query”: {

“match_phrase”: {

“remark”: “java assistant”

}

}

}

复制代码

**-1)、 match phrase原理 – term position **

ES是如何实现match phrase短语搜索的?其实在ES中,使用match phrase做搜

索的时候,也是和match类似,首先对搜索条件进行分词-analyze。将搜索条件拆

分成hello和world。既然是分词后再搜索,ES是如何实现短语搜索的?

这里涉及到了倒排索引的建立过程。在倒排索引建立的时候,ES会先对

document数据进行分词,如:

GET _analyze

{

“text”: “hello world, java spark”,

“analyzer”: “standard”

}

复制代码

分词的结果是:

{

“tokens”: [

{

“token”: “hello”,

“start_offset”: 0,

“end_offset”: 5,

“type”: “”,

“position”: 0

},

{

“token”: “world”,

“start_offset”: 6,

“end_offset”: 11,

“type”: “”,

“position”: 1

},

{

“token”: “java”,

“start_offset”: 13,

“end_offset”: 17,

“type”: “”,

“position”: 2

},

{

“token”: “spark”,

“start_offset”: 18,

“end_offset”: 23,

“type”: “”,

“position”: 3

}

]

}

复制代码

从上述结果中,可以看到。ES在做分词的时候,除了将数据切分外,还会保留

一个position。position代表的是这个词在整个数据中的下标。当ES执行match

phrase搜索的时候,首先将搜索条件hello world分词为hello和world。然后在倒

排索引中检索数据,如果hello和world都在某个document的某个field出现时,那么检查这两个匹配到的单词的position是否是连续的,如果是连续的,代表匹配成

功,如果是不连续的,则匹配失败。

**-2). match phrase搜索参数 – slop **

在做搜索操作的是,如果搜索参数是hello spark。而ES中存储的数据是hello

world, java spark。那么使用match phrase则无法搜索到。在这个时候,可以使

用match来解决这个问题。但是,当我们需要在搜索的结果中,做一个特殊的要

求:hello和spark两个单词距离越近,document在结果集合中排序越靠前,这个时

候再使用match则未必能得到想要的结果。

ES的搜索中,对match phrase提供了参数slop。slop代表match phrase短语搜

索的时候,单词最多移动多少次,可以实现数据匹配。在所有匹配结果中,多个单

词距离越近,相关度评分越高,排序越靠前。

这种使用slop参数的match phrase搜索,就称为近似匹配(proximity

search)

如:

数据为: hello world, java spark

搜索为: match phrase : hello spark。

slop为: 3 (代表单词最多移动3次。)

执行短语搜索的时候,将条件hello spark分词为hello和spark两个单词。并

且连续。

hello spark

接下来,可以根据slop参数执行单词的移动。

| 下标 | 0 | 1 | 2 | 3 |

| — | — | — | — | — |

| doc | hello | world | java | spark |

| 搜索 | hello | spark | | |

| 移动 | hello | | spark | |

| 移动2 | hello | | | spark |

匹配成功,不需要移动第三次即可匹配。

如果:

数据为: hello world, java spark

搜索为: match phrase : spark hello。

slop为: 5 (代表单词最多移动5次。)执行短语搜索的时候,将条件hello spark分词为hello和spark两个单词。并且连续。

spark hello

接下来,可以根据slop参数执行单词的移动。

下标 : 0 1 2 3

doc : hello world java spark

搜索 : spark hello

移动1: spark/hello

移动2: hello spark

移动3: hello spark

移动4: hello spark

匹配成功,不需要移动第五次即可匹配。

如果当slop移动次数使用完毕,还没有匹配成功,则无搜索结果。如果使用中文分词,则

移动次数更加复杂,因为中文词语有重叠情况,很难计算具体次数,需要多次尝试才行。

测试案例:

英文:

GET _analyze

{

“text”: “hello world, java spark”,

“analyzer”: “standard”

}

POST /test_a/_doc/3

{

“f” : “hello world, java spark”

}

GET /test_a/_search

{

“query”: {

“match_phrase”: {

“f” : {

“query”: “hello spark”,

“slop” : 2

}

}

}

}

GET /test_a/_search

{

“query”: {

“match_phrase”: {

“f” : {

“query”: “spark hello”,

“slop” : 4

}

}

}

}

复制代码

**中文: **

GET _analyze

{

“text”: “中国,一个世界上最强的国家”,

“analyzer”: “ik_max_word”

}

POST /test_a/_doc/1

{

“f” : “中国,一个世界上最强的国家”

}

GET /test_a/_search

{

“query”: {

“match_phrase”: {

“f” : {

“query”: “中国最强”,

“slop” : 5

}

}

}

}

GET /test_a/_search

{

“query”: {

“match_phrase”: {

“f” : {

“query”: “最强中国”,

“slop” : 9

}

}

}

读者福利

分享一份自己整理好的Java面试手册,还有一些面试题pdf

不要停下自己学习的脚步

字节跳动的面试分享,为了拿下这个offer鬼知道我经历了什么

字节跳动的面试分享,为了拿下这个offer鬼知道我经历了什么

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
spark两个单词。并且连续。

spark hello

接下来,可以根据slop参数执行单词的移动。

下标 : 0 1 2 3

doc : hello world java spark

搜索 : spark hello

移动1: spark/hello

移动2: hello spark

移动3: hello spark

移动4: hello spark

匹配成功,不需要移动第五次即可匹配。

如果当slop移动次数使用完毕,还没有匹配成功,则无搜索结果。如果使用中文分词,则

移动次数更加复杂,因为中文词语有重叠情况,很难计算具体次数,需要多次尝试才行。

测试案例:

英文:

GET _analyze

{

“text”: “hello world, java spark”,

“analyzer”: “standard”

}

POST /test_a/_doc/3

{

“f” : “hello world, java spark”

}

GET /test_a/_search

{

“query”: {

“match_phrase”: {

“f” : {

“query”: “hello spark”,

“slop” : 2

}

}

}

}

GET /test_a/_search

{

“query”: {

“match_phrase”: {

“f” : {

“query”: “spark hello”,

“slop” : 4

}

}

}

}

复制代码

**中文: **

GET _analyze

{

“text”: “中国,一个世界上最强的国家”,

“analyzer”: “ik_max_word”

}

POST /test_a/_doc/1

{

“f” : “中国,一个世界上最强的国家”

}

GET /test_a/_search

{

“query”: {

“match_phrase”: {

“f” : {

“query”: “中国最强”,

“slop” : 5

}

}

}

}

GET /test_a/_search

{

“query”: {

“match_phrase”: {

“f” : {

“query”: “最强中国”,

“slop” : 9

}

}

}

读者福利

分享一份自己整理好的Java面试手册,还有一些面试题pdf

不要停下自己学习的脚步

[外链图片转存中…(img-1nUe0u4s-1714475328247)]

[外链图片转存中…(img-031YDlOb-1714475328248)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值