目录
- 一、浅析Elasticsearch架构原理
- 1.Elasticsearch的节点类型
- 1.1 Master节点
- 1.2DataNode节点
- 二、分片和副本机制
- 2.1分片
- 2.2副本
- 2.3指定分片、副本数量
- 2.4查看分片、主分片、副本分片
- 三、Elasticsearch工作流程
- 3.1Elasticsearch文档写入原理
- 3.2Elasticsearch检索原理
- 四、Elasticsearch准实时索引实现
- 4.1溢写到文件系统缓存
- 4.2写translog保障容错
- 4.3flush到磁盘(刷盘)
- 4.4segment合并
- 五.手动控制搜索结果精准度
- 5.1operator与minimum_should_match简单使用
- 5.2、match 的底层转换
- 5.3、boost权重控制
- 5.4、基于dis_max实现best fields策略进行多字段搜索
- 5.5、基于tie_breaker参数优化dis_max搜索效果
- 5.6、使用multi_match简化dis_max+tie_breaker(不常用)
- 5.7、cross_fields搜索
- 5.8、copy_to组合fields
- 5.9、近似匹配
- 5.10、match_phrase
- 5.10.1match phrase原理 —— term position
- 5.10.2match phrase搜索参数 -- slop
- 5.11使用match和proximity search实现召回率和精准度平衡。
- 5.12前缀搜索 prefix search
- 5.13通配符搜索
- 5.14正则搜索
- 5.15搜索推荐
- 5.16fuzzy模糊搜索技术
一、浅析Elasticsearch架构原理
1.Elasticsearch的节点类型
在Elasticsearch主要分成两类节点,一类是Master,一类是DataNode。
1.1 Master节点
在Elasticsearch启动时,会选举出来一个Master节点。采用Zen Discovery1机制选出master节点并且找到集群中的其他节点,并建立连接。一个Elasticsearch集群中,只有一个Master节点。(这里的一个是在集群范围中的,而不是指定某台服务器一直就是主节点,主节点所在服务器宕机,其他的某一个节点有机会成为master节点)
Master节点主要功能::
- 管理索引和分片的创建、删除和重新分配。
- 监测节点的状态,并在需要时进行重分配。
- 协调节点之间的数据复制和同步工作。
- 处理集群级别操作,如创建或删除索引、添加或删除节点等。
- 维护集群的健康状态,并在集群出现问题时采取措施解决。
- 维护元数据2
1.2DataNode节点
与master节点不同,datanode节点可能会有多个。这个取决于你集群的节点数量,因为master在集群中只能有一个,其余为DataNode节点。
DataNode节点主要功能:
- 存储和索引数据:Data Node 节点会将索引分片存储在本地磁盘上,并对查询请求进行响应。
- 复制和同步数据:为了确保数据的可靠性和高可用性,ElasticSearch 会将每个原始分片的多个副本存储在不同的 Data Node 节点上,并定期将各节点上的数据进行同步。
- 参与搜索和聚合操作:当客户端提交搜索请求时,Data Node 节点会使用本地缓存和分片数据完成搜索和聚合操作。
- 执行数据维护操作:例如,清理过期数据和压缩分片等。
二、分片和副本机制
在第一篇文章中也有介绍过这俩个概念,这里在集群中再次进行解释
2.1分片
ElasticSearch是一个分布式的搜索引擎,索引索引可以分成一份或多份,多份分片分布在不同节点当中。ElasticSearch会自动管理分片,如果发现分片分布不均衡,就会自动迁移。
2.2副本
在ElasticSearch中每个分片都有一个主分片,可能会有若干个副本分片(默认一个分片,一个副本),这些副本也会分布在不同的节点上。
2.3指定分片、副本数量
PUT /test_index06
{
"mappings": {
"properties": {
"name": {
"type": "keyword",
"index": true,
"store": true
},
................
}
},
//设置分片数量1,副本数量2
"settings": {
"number_of_shards": 1,
"number_of_replicas": 2
}
}
2.4查看分片、主分片、副本分片
GET /_cat/indices?v
三、Elasticsearch工作流程
3.1Elasticsearch文档写入原理
如何知道我插入一条数据要保存到那个分片呢?
shard = hash(routing) % number_of_primary_shards
routing 是一个可变值,默认是文档的 _id。
number_of_primary_shards为分片数量
你也可以使用自己的自定义分片键,只需在索引时指定"_routing"字段即可。
3.2Elasticsearch检索原理
客户端发起查询请求,某个DataNode接收到请求,该DataNode就会成为协调节点。
协调节点(Coordinating Node)将查询请求广播到每一个数据节点,这些数据节点的分片会处理该查询请求, 每个分片进行数据查询,将符合条件的数据放在一个优先队列中,并将这些数据的文档ID、节点信息、分片信息返回给协调节点。
协调节点将所有的结果进行汇总,并进行全局排序, 协调节点向包含这些文档ID的分片发送get请求,对应的分片将文档数据返回给协调节点,最后协调节点将数据返回给客户端。
四、Elasticsearch准实时索引实现
4.1溢写到文件系统缓存
当数据写入到ES分片时,会首先写入到内存中,然后通过内存的buffer生成一个segment,并刷到文件系统缓存中,数据可以被检索(注意不是直接刷到磁盘)ES中默认1秒,refresh缓存一次。
4.2写translog保障容错
在写入到内存中的同时,也会记录translog日志,在refresh期间出现异常,会根据translog来进行数据恢复
等到文件系统缓存中的segment数据都刷到磁盘中,清空translog文件。
4.3flush到磁盘(刷盘)
ES默认每隔30分钟会将文件系统缓存的数据刷入到磁盘。
4.4segment合并
Segment太多时,ES定期会将多个segment合并成为大的segment,减少索引查询时IO开销,此阶段ES会真正的物理删除(之前执行过的delete的数据)。
五.手动控制搜索结果精准度
5.1operator与minimum_should_match简单使用
①查询document中的remark字段包含java或developer词组。
GET /test_index05/_search
{
"query": {
"match": {
"remark": "java developer"
}
}
}
或者这样查询
GET /test_index05/_search
{
"query": {
"match": {
"remark": {
"query": "java developer",
"operator": "or"
}
}
}
}
结果
"hits" : [
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.77041256,
"_source" : {
"name" : "宝塔镇河妖",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java developer"
}
},
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.21110919,
"_source" : {
"name" : "天王盖地虎",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java"
}
}
]
②查询document中的remark字段,同时包含java和developer词组
GET /test_index05/_search
{
"query": {
"match": {
"remark": {
"query": "java developer",
"operator": "and"
}
}
}
}
结果
"hits" : [
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.77041256,
"_source" : {
"name" : "宝塔镇河妖",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java developer"
}
}
]
③minimum_should_match可以使用百分比或固定数字。百分比代表query搜索条件中词条百分比,如果无法整除,向下匹配(如,query条件有3个单词,如果使用百分比提供精准度计算,那么是无法除尽的,如果需要至少匹配两个单词,则需要用67%来进行描述。如果使用66%描述,ES则认为匹配一个单词即可)。固定数字代表query搜索条件中的词条,至少需要匹配多少个。
③-1百分比
查询内容包括java 或developer或assistant中匹配度达到66%
即文档内容中,至少包括一个单词出现。
GET /test_index05/_search
{
"query": {
"match": {
"remark": {
"query": "java developer assistant",
"minimum_should_match": "66%"
}
}
}
}
"hits" : [
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.21110919,
"_source" : {
"name" : "天王盖地虎",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java"
}
},
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.160443,
"_source" : {
"name" : "宝塔镇河妖",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java developer"
}
}
]
查询内容包括java 或architect 或assistant中匹配度达到67%
即文档内容中,至少包括两个个单词出现。
GET /test_index05/_search
{
"query": {
"match": {
"remark": {
"query": "java developer assistant",
"minimum_should_match": "67%"
}
}
}
}
结果
"hits" : [
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.77041256,
"_source" : {
"name" : "宝塔镇河妖",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java developer"
}
}
]
③-2固定数字
查询的内容中至少出现下面三个条件中的两个,即java、developer、assistant这三个单词,至少有两个同时出现才符合条件。
GET /test_index05/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"remark": "java"
}
},
{
"match": {
"remark": "developer"
}
},
{
"match": {
"remark": "assistant"
}
}
],
"minimum_should_match": 2
}
}
}
结果
"hits" : [
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.77041256,
"_source" : {
"name" : "宝塔镇河妖",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java developer"
}
}
]
5.2、match 的底层转换
我们输入的查询语句
GET /test_index05/_search
{
"query": {
"match": {
"remark": "java developer"
}
}
}
转换后的查询语句
GET /test_index05/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"remark": "java"
}
},
{
"term": {
"remark": {
"value": "developer"
}
}
}
]
}
}
}
查询语句
GET /test_index05/_search
{
"query": {
"match": {
"remark": {
"query": "java developer",
"operator": "and"
}
}
}
}
转换后
GET /test_index05/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"remark": "java"
}
},
{
"term": {
"remark": {
"value": "developer"
}
}
}
]
}
}
}
查询条件
GET /test_index05/_search
{
"query": {
"match": {
"remark": {
"query": "java developer assistant",
"minimum_should_match": "68%"
}
}
}
}
转换后
GET /test_index05/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"remark": "java"
}
},
{
"term": {
"remark": "developer"
}
},
{
"term": {
"remark": "assistant"
}
}
],
"minimum_should_match": 2
}
}
}
使用转换后的语法执行搜索,效率更高。
5.3、boost权重控制
搜索document中remark字段中包含java的数据,如果remark中包含developer或assistant,则包含assistant的document优先显示。(就是将assistant数据匹配时的相关度分数增加)。
一般用于搜索时相关度排序使用。如:电商中的综合排序。将一个商品的销量,广告投放,评价值,库存,单价比较综合排序。在上述的排序元素中,广告投放权重最高,库存权重最低。还有就是百度搜索内容时前几一般都是广告。
例如
索引test_index05下全部数据为
"hits" : [
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "天王盖地虎",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java"
}
},
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"name" : "宝塔镇河妖",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java developer"
}
},
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"name" : "铁锅炖大鹅",
"sex" : 1,
"age" : 19,
"address" : "天津",
"remark" : "java assistant"
}
}
]
查询(boost越高,表示权重越高,越优先展示)
GET /test_index05/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"remark": "java"
}
}
],
"should": [
{
"match": {
"remark": {
"query": "developer",
"boost": 1
}
}
},
{
"match": {
"remark": {
"query": "assistant",
"boost": 3
}
}
}
]
}
}
}
结果
"hits" : [
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "3",
"_score" : 2.3016074,
"_source" : {
"name" : "铁锅炖大鹅",
"sex" : 1,
"age" : 19,
"address" : "天津",
"remark" : "java assistant"
}
},
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.474477,
"_source" : {
"name" : "宝塔镇河妖",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java developer"
}
},
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.43250346,
"_source" : {
"name" : "天王盖地虎",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java"
}
}
]
查询
GET /test_index05/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"remark": "java"
}
}
],
"should": [
{
"match": {
"remark": {
"query": "developer",
"boost": 2
}
}
},
{
"match": {
"remark": {
"query": "assistant",
"boost": 1
}
}
}
]
}
}
}
结果
"hits" : [
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "2",
"_score" : 2.611973,
"_source" : {
"name" : "宝塔镇河妖",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java developer"
}
},
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.9918565,
"_source" : {
"name" : "铁锅炖大鹅",
"sex" : 1,
"age" : 19,
"address" : "天津",
"remark" : "java assistant"
}
},
{
"_index" : "test_index05",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.43250346,
"_source" : {
"name" : "天王盖地虎",
"sex" : 1,
"age" : 25,
"address" : "上海",
"remark" : "java"
}
}
]
5.4、基于dis_max实现best fields策略进行多字段搜索
best_fields策略: 搜索的document中的某一个field,尽可能多的匹配搜索条件。
most_fields策略:与best fields相反的是,尽可能多的字段匹配到搜索条件。
dis_max语法: 直接获取搜索的多条件中的,单条件query相关度分数最高的数据,以这个数据做相关度排序。
best fields策略实现举例(是找name字段中’秀儿’匹配相关度分数或remark字段中’java developer’匹配相关度分数,哪个高,就使用哪一个相关度分数进行结果排序。)
GET /test_index06/_search
{
"query": {
"dis_max": {
"queries": [
{
"match": {
"remark": "java developer"
}
},
{
"match": {
"name": "秀儿"
}
}
]
}
}
}
结果
"hits" : [
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vkjNUIcB14FuHovqnIz1",
"_score" : 2.3842063,
"_source" : {
"name" : "秀儿",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vEjNUIcB14FuHovqKIyb",
"_score" : 1.781607,
"_source" : {
"name" : "rod",
"sex" : 1,
"age" : 25,
"book" : "Spring",
"remark" : "java developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vUjNUIcB14FuHovqaYzA",
"_score" : 0.24116206,
"_source" : {
"name" : "rods",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "python developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "v0jOUIcB14FuHovqTYw0",
"_score" : 0.24116206,
"_source" : {
"name" : "Tom",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "wEjOUIcB14FuHovqYYw0",
"_score" : 0.24116206,
"_source" : {
"name" : "Amy",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
}
]
5.5、基于tie_breaker参数优化dis_max搜索效果
我不想根据打分最高的那个字段进行排序展示,我想让其他的字段也参与进来咋办?
dis_max是将多个搜索query条件中相关度分数最高的用于结果排序,忽略其他query分数,在某些情况下,可能还需要其他query条件中的相关度介入最终的结果排序,这个时候可以使用tie_breaker参数来优化dis_max搜索。tie_breaker参数代表的含义是:将其他query搜索条件的相关度分数乘以参数值,再参与到结果排序中。如果不定义此参数,相当于参数值为0。所以其他query条件的相关度分数被忽略。
tie_breaker指定的值最大为1,除最高分字段,设置其他字段打分的策略,即其他字段得分乘指定的系数,如果不加这个tie_breaker则默认为0
GET /test_index06/_search
{
"query": {
"dis_max": {
"queries": [
{
"match": {
"remark": "java developer"
}
},
{
"match": {
"name": "秀儿"
}
}
],
"tie_breaker": 0.5
}
}
}
结果
"hits" : [
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vkjNUIcB14FuHovqnIz1",
"_score" : 2.5047874,
"_source" : {
"name" : "秀儿",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vEjNUIcB14FuHovqKIyb",
"_score" : 1.781607,
"_source" : {
"name" : "rod",
"sex" : 1,
"age" : 25,
"book" : "Spring",
"remark" : "java developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vUjNUIcB14FuHovqaYzA",
"_score" : 0.24116206,
"_source" : {
"name" : "rods",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "python developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "v0jOUIcB14FuHovqTYw0",
"_score" : 0.24116206,
"_source" : {
"name" : "Tom",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "wEjOUIcB14FuHovqYYw0",
"_score" : 0.24116206,
"_source" : {
"name" : "Amy",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
}
]
5.6、使用multi_match简化dis_max+tie_breaker(不常用)
ES中相同结果的搜索也可以使用不同的语法语句来实现。
查询方式1
GET /test_index06/_search
{
"query": {
"dis_max": {
"queries": [
{
"match": {
"name": "Tom"
}
},
{
"match": {
"remark": {
"query": "java developer",
"boost": 2,
"minimum_should_match": 2
}
}
}
],
"tie_breaker": 0.5
}
}
}
结果
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 3.563214,
"hits" : [
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vEjNUIcB14FuHovqKIyb",
"_score" : 3.563214,
"_source" : {
"name" : "rod",
"sex" : 1,
"age" : 25,
"book" : "Spring",
"remark" : "java developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "v0jOUIcB14FuHovqTYw0",
"_score" : 1.6360589,
"_source" : {
"name" : "Tom",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
}
]
查询方式2(其中type常用的有best_fields和most_fields。^n代表权重,相当于"boost":n。)
GET /test_index06/_search
{
"query": {
"multi_match": {
"query": "Tom java developer",
"fields": [
"name",
"remark^2"
],
"type": "best_fields",
"tie_breaker": 0.5,
"minimum_should_match": "50%"
}
}
}
结果
"hits" : [
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vEjNUIcB14FuHovqKIyb",
"_score" : 3.563214,
"_source" : {
"name" : "rod",
"sex" : 1,
"age" : 25,
"book" : "Spring",
"remark" : "java developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "v0jOUIcB14FuHovqTYw0",
"_score" : 1.877221,
"_source" : {
"name" : "Tom",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vUjNUIcB14FuHovqaYzA",
"_score" : 0.48232412,
"_source" : {
"name" : "rods",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "python developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vkjNUIcB14FuHovqnIz1",
"_score" : 0.48232412,
"_source" : {
"name" : "秀儿",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "wEjOUIcB14FuHovqYYw0",
"_score" : 0.48232412,
"_source" : {
"name" : "Amy",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
}
]
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搜索策略
例如(搜索条件中的java必须在name或remark字段中匹配,developer也必须在name或remark字段中匹配。
)
GET /test_index06/_search
{
"query": {
"multi_match": {
"query": "java developer",
"fields": [
"name",
"remark"
],
"type": "cross_fields",
"operator": "and"
}
}
}
结果
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.781607,
"hits" : [
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vEjNUIcB14FuHovqKIyb",
"_score" : 1.781607,
"_source" : {
"name" : "rod",
"sex" : 1,
"age" : 25,
"book" : "Spring",
"remark" : "java developer"
}
}
]
most field策略问题:most fields策略是尽可能匹配更多的字段,所以会导致精确搜索结果排序问题。又因为cross fields搜索,不能使用minimum_should_match来去除长尾数据。所以在使用most fields和cross fields策略搜索数据的时候,都有不同的缺陷。所以商业项目开发中,都推荐使用best fields策略实现搜索。
5.8、copy_to组合fields
场景:在电商网站,如果在搜索框中输入“手机”,点击搜索,那么是在商品的类型名称、商品的名称、商品的卖点、商品的描述等字段中,哪一个字段内进行数据的匹配?如果使用某一个字段做搜索不合适,那么使用_all做搜索是否合适?也不合适,因为_all字段中可能包含图片,价格等字段。
假设,有一个字段,其中的内容包括(但不限于):商品类型名称、商品名称、商品卖点等字段的数据内容。是否可以在这个特殊的字段上进行数据搜索匹配?(我理解的就是融合多个字段为一个,理解为该商品的摘要信息。)
以keyword字段举例,它包括了category_name、product_name、sell_point三个字段的内容。
{
"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映射策略。
例如
PUT /test_index07/_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、近似匹配
给定一个短语,或者单词,匹配包含全部或者部分的内容。
举例(test_index06中remark字段没有go相关内容)
GET /test_index06/_search
{
"query": {
"match": {
"remark": "developer go"
}
}
}
结果
"hits" : [
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vEjNUIcB14FuHovqKIyb",
"_score" : 0.24116206,
"_source" : {
"name" : "rod",
"sex" : 1,
"age" : 25,
"book" : "Spring",
"remark" : "java developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vUjNUIcB14FuHovqaYzA",
"_score" : 0.24116206,
"_source" : {
"name" : "rods",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "python developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vkjNUIcB14FuHovqnIz1",
"_score" : 0.24116206,
"_source" : {
"name" : "秀儿",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "v0jOUIcB14FuHovqTYw0",
"_score" : 0.24116206,
"_source" : {
"name" : "Tom",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
},
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "wEjOUIcB14FuHovqYYw0",
"_score" : 0.24116206,
"_source" : {
"name" : "Amy",
"sex" : 1,
"age" : 26,
"book" : "Spring",
"remark" : "C developer"
}
}
]
举例
GET /test_index06/_search
{
"query": {
"match": {
"remark": "developerAA"
}
}
}
结果
"hits" : [ ]
如果需要的结果是有特殊要求,如:java developer 必须是一个完整的短语,不可分割;或document中的field内,包含的java 和developer 单词,且两个单词之间离的越近,相关度分数越高。那么这种特殊要求的搜索就是近似搜索。 搜索包括javb内容,搜索条件在java developer数据中搜索,或包括 j 搜索提示等数据近似搜索的一部分。如何上述特殊要求的搜索,使用match搜索语法就无法实现了。
5.10、match_phrase
短语搜索。就是搜索条件不分词。代表搜索条件不可分割。
举例(只会匹配出,java developer同时出现,并且连续的内容,即java developer为一个整体出现)
GET /test_index06/_search
{
"query": {
"match_phrase": {
"remark": "java developer"
}
}
}
结果
"hits" : [
{
"_index" : "test_index06",
"_type" : "_doc",
"_id" : "vEjNUIcB14FuHovqKIyb",
"_score" : 1.7816072,
"_source" : {
"name" : "rod",
"sex" : 1,
"age" : 25,
"book" : "Spring",
"remark" : "java developer"
}
}
]
5.10.1match 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" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "world",
"start_offset" : 6,
"end_offset" : 11,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "java",
"start_offset" : 13,
"end_offset" : 17,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "spark",
"start_offset" : 18,
"end_offset" : 23,
"type" : "<ALPHANUM>",
"position" : 3
}
]
}
从上述结果中,可以看到。ES在做分词的时候,除了将数据切分外,还会保留一个position。position代表的是这个词在整个数据中的下标。当ES执行match phrase搜索的时候,首先将搜索条件hello world分词为hello和world。然后在倒排索引中检索数据,如果hello和world都在某个document的某个field出现时,那么检查这两个匹配到的单词的position是否是连续的,如果是连续的,代表匹配成功,如果是不连续的,则匹配失败。
5.10.2match 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)
在Elasticsearch中,slop是指在查询语句中,词项之间可以允许的最大距离。它是一种模糊匹配(fuzzy matching)方式,用于解决用户输入错误或者数据存储时不准确的情况。
当我们进行一个带有slop参数的查询时,Elasticsearch将按照文档中出现的顺序检查查询语句中的每个词,并尝试找到它们之间最接近的匹配。如果两个词之间的距离小于或等于slop的值,则它们被认为是匹配的。
例子:
假设我们有以下三个文档:
{
"id": 1,
"title": "quick brown fox"
}
{
"id": 2,
"title": "quick red fox"
}
{
"id": 3,
"title": "slow brown dog"
}
我们希望查找包含“quick”和“fox”的文档,并且它们之间的最大距离为1。我们可以使用以下查询:
{
"query": {
"match_phrase": {
"title": {
"query": "quick fox",
"slop": 1
}
}
}
}
该查询将返回文档1和2,但不会返回文档3,因为“slow”和“brown”之间的距离大于1。(关于距离你可以去看下对应的position字段,即match phrase原理 —— term position下讲解的内容)
需要注意的是,slop值越大,匹配的结果会越多,但是精度也会降低。因此,在使用slop时,需要根据实际情况进行权衡。
5.11使用match和proximity search实现召回率和精准度平衡。
召回率:召回率就是搜索结果比率,如:索引A中有100个document,搜索时返回多少个document,就是召回率(recall)。
精准度:就是搜索结果的准确率,如:搜索条件为hello java,在搜索结果中尽可能让短语匹配和hello java离的近的结果排序靠前,就是精准度(precision)。
如果在搜索的时候,只使用match phrase语法,会导致召回率低下,因为搜索结果中必须包含短语(包括proximity search)。
如果在搜索的时候,只使用match语法,会导致精准度底下,因为搜索结果排序是根据相关度分数算法计算得到。
那么如果需要在结果中兼顾召回率和精准度的时候,就需要将match和proximity search混合使用,来得到搜索结果。
索引test_index08下所有内容
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"f" : "hello, java is very good, spark is also very good"
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"f" : "java and spark, development language "
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "5",
"_score" : 1.0,
"_source" : {
"f" : "Java Spark is a fast and general-purpose cluster computing system. It provides high-level APIs in Java, Scala, Python and R, and an optimized engine that supports general execution graphs."
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "6",
"_score" : 1.0,
"_source" : {
"f" : "java spark and, development language "
}
}
]
查询1
GET /test_index08/_search
{
"query": {
"match": {
"f": "java spark"
}
}
}
结果
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 0.28046143,
"hits" : [
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.28046143,
"_source" : {
"f" : "java and spark, development language "
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "6",
"_score" : 0.28046143,
"_source" : {
"f" : "java spark and, development language "
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.23111339,
"_source" : {
"f" : "hello, java is very good, spark is also very good"
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "5",
"_score" : 0.16973917,
"_source" : {
"f" : "Java Spark is a fast and general-purpose cluster computing system. It provides high-level APIs in Java, Scala, Python and R, and an optimized engine that supports general execution graphs."
}
}
]
查询2
GET /test_index08/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"f": "java spark"
}
}
],
"should": [
{
"match_phrase": {
"f": {
"query": "java spark",
"slop": 50
}
}
}
]
}
}
}
结果
"hits" : [
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "6",
"_score" : 0.56092286,
"_source" : {
"f" : "java spark and, development language "
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.4815065,
"_source" : {
"f" : "java and spark, development language "
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.32339638,
"_source" : {
"f" : "hello, java is very good, spark is also very good"
}
},
{
"_index" : "test_index08",
"_type" : "_doc",
"_id" : "5",
"_score" : 0.30782324,
"_source" : {
"f" : "Java Spark is a fast and general-purpose cluster computing system. It provides high-level APIs in Java, Scala, Python and R, and an optimized engine that supports general execution graphs."
}
}
]
5.12前缀搜索 prefix search
使用前缀匹配实现搜索能力。通常针对keyword类型字段,也就是不分词的字段。
GET /test_a/_mapping
{
"test_a" : {
"mappings" : {
"properties" : {
"f" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
所有数据如下
"hits" : [
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"f" : "hello, java is very good, spark is also very good"
}
},
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"f" : "java and spark, development language "
}
}
]
查询
GET /test_a/_search
{
"query": {
"prefix": {
"f.keyword": {
"value": "j"
}
}
}
}
结果
"hits" : [
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"f" : "java and spark, development language "
}
}
]
查询
GET /test_a/_search
{
"query": {
"prefix": {
"f.keyword": {
"value": "J"
}
}
}
}
结果
"hits" : [ ]
针对前缀搜索,是对keyword类型字段而言。而keyword类型字段数据大小写敏感。前缀搜索效率比较低。前缀搜索不会计算相关度分数。前缀越短,效率越低。如果使用前缀搜索,建议使用长前缀。因为前缀搜索需要扫描完整的索引内容,所以前缀越长,相对效率越高。
5.13通配符搜索
通配符可以在倒排索引中使用,也可以在keyword类型字段中使用。
(性能很低,也是需要扫描完整的索引)
?
:表示一个任意字符
*
·:表示0~n个任意字符
查询
GET /test_a/_search
{
"query": {
"wildcard": {
"f.keyword": {
"value": "?e*o*"
}
}
}
}
结果
"hits" : [
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"f" : "hello, java is very good, spark is also very good"
}
}
]
5.14正则搜索
在Elasticsearch中,regexp查询是一种使用正则表达式进行搜索的查询方式。它可以在指定字段上匹配满足正则表达式的文本。
ES支持正则表达式,可以在倒排索引或keyword类型字段中使用。
例如,假设我们有一个包含文档标题和内容的索引,并想要查找所有标题或内容中包含“Elastic”和“search”的文档。这时候,就可以使用regexp查询来实现:
{
"query": {
"regexp": {
"_all": ".*Elastic.*search.*"
}
}
}
在上述例子中,“_all” 表示对所有字段进行搜索,".*"表示任意字符出现0次或多次。
需要注意的是,正则表达式的查询效率较低,因为它需要对每个文档的每个字段都进行逐一匹配。如果对性能要求较高,应该尽量避免使用正则表达式查询。
另外,Elasticsearch还支持设置正则表达式的参数,如ignore_case(是否忽略大小写),max_determinized_states(最大化自动机状态数),boost(权重系数),以及flags(正则表达式标志)。这些参数可以提高查询的准确性和灵活性。
再举例
GET /test_a/_search
{
"query": {
"regexp": {
"f.keyword": "[A-z].+"
}
}
}
结果
"hits" : [
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"f" : "hello, java is very good, spark is also very good"
}
},
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"f" : "java and spark, development language "
}
}
]
性能很低,需要扫描完整索引,应该尽量避免在大型索引中使用
5.15搜索推荐
在Elasticsearch中,match_phrase_prefix查询是一种结合了match和prefix两种查询的组合查询。它可以用于匹配以指定前缀开头的短语。
具体来说,match_phrase_prefix查询会先将查询字符串拆分成一个个词项(term),然后使用前缀匹配算法进行匹配。通常情况下,match_phrase_prefix查询适用于需要匹配长短语但又希望支持前缀匹配的场景。
搜索推荐: search as your type, 搜索提示。如:索引中有若干数据以“hello”开头,那么在输入hello的时候,推荐相关信息。(类似百度输入框)
查询
GET /test_a/_search
{
"query": {
"match_phrase_prefix": {
"f": {
"query": "java s",
"slop": 10,
"max_expansions": 10
}
}
}
}
结果
"hits" : [
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.28650534,
"_source" : {
"f" : "java and spark, development language "
}
},
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.11460209,
"_source" : {
"f" : "hello, java is very good, spark is also very good"
}
}
]
其原理和match phrase类似,是先使用match匹配term数据(java),然后在指定的slop移动次数范围内,前缀匹配(s),max_expansions是用于指定prefix最多匹配多少个term(单词),超过这个数量就不再匹配了。
这种语法的限制是,只有最后一个term会执行前缀搜索。
执行性能很差,最后一个term是需要扫描所有符合slop要求的倒排索引的term。
因为效率较低,如果必须使用,则一定要使用参数max_expansions。
5.16fuzzy模糊搜索技术
Elasticsearch中的fuzzy模糊搜索技术是一种基于编辑距离(Levenshtein Distance)算法的全文检索技术。它允许在查询时匹配相似但不完全相同的单词。具体来说,当我们进行一个fuzzy query时,Elasticsearch将会在索引中查找与查询字符串最接近的项。如果查询字符串中有一个拼写错误或者一个字符丢失,fuzzy search可以帮助我们找到那些被错误拼写的项。另外,fuzzy search也能够在搜索时匹配多个单词之间的相似性。在Elasticsearch中,我们可以使用fuzziness参数来设置模糊度,该参数表示最大编辑距离,即允许的最大差异数量。默认值为2,这意味着如果两个单词的编辑距离超过2,则它们将不会被匹配。我们可以通过增加或减少该参数来调整模糊度,以便更好地满足我们的需求。
搜索的时候,可能搜索条件文本输入错误,如:hello world -> hello word。这种拼写错误还是很常见的。fuzzy技术就是用于解决错误拼写的(在英文中很有效,在中文中几乎无效。)。其中fuzziness代表value的值word可以修改多少个字母来进行拼写错误的纠正(修改字母的数量包含字母变更,增加或减少字母。)。f代表要搜索的字段名称。
查询
GET /test_a/_search
{
"query": {
"fuzzy": {
"f": {
"value": "word",
"fuzziness": 2
}
}
}
}
结果
"hits" : [
{
"_index" : "test_a",
"_type" : "_doc",
"_id" : "3",
"_score" : 0.43569255,
"_source" : {
"f" : "hello, java is very good, spark is also very good"
}
}
]
Zen Discovery 是 Elasticsearch 中的一种自动发现机制,它用于在分布式环境下管理节点的发现和连接。Zen Discovery 能够自动感知节点的加入和离开,并在必要时重新分配数据和重新平衡群集。
Zen Discovery 机制包括以下几个方面:
1.Ping 操作:每个节点会定期向其他节点发送 ping 请求,以确定其他节点是否还在运行。如果一个节点在一定时间内没有响应,那么它就被认为已经离开了群集。
2.Unicast 发现:节点之间可以通过互相发送地址列表来进行发现。在这种方式下,节点需要知道其他节点的 IP 地址和端口号,才能够加入群集。当节点启动时,它会向配置的节点列表发送加入请求,如果请求成功,则会将该节点加入群集。
3.Multicast 发现:在使用 Multicast 发现机制时,节点可以通过多播地址来进行发现。每个节点将自己的 IP 地址和端口号发布到特定的多播地址上,其他节点可以从该地址上接收到所有节点的信息,从而发现新的节点。这种方式下,节点可以更加灵活地管理群集,可以随时加入和离开群集。
4.Master 选举:Zen Discovery 还包括了 Master 节点的选举机制,选举出的 Master 节点会负责协调群集中的各个节点。
(当然,在某些情况下,可能需要手动指定主节点或禁用主节点竞选过程。可以通过在 elasticsearch.yml 配置文件中设置 node.master 参数来实现。如果将该参数设置为 false,则表示禁用该节点的主节点竞选功能;如果将该参数设置为 true,则表示该节点可以参与主节点竞选。默认情况下,所有节点都会参与主节点竞选,因此无需手动配置。) ↩︎在 ElasticSearch 集群中,Master 节点维护的元数据包括以下信息:
1.集群状态:保存了当前集群的状态,如运行状态、健康状态等。
2.索引元数据:保存了所有索引的信息,例如字段映射、分片数量、副本数量、索引别名等。
3.节点元数据:保存了所有节点的信息,例如 IP 地址、节点名称、可用空间、JVM 信息等。
4.分片分配信息:保存了每个分片所属的节点信息、是否是主分片等。
5.节点故障检测信息:保存了节点最近一次的心跳信息和下线时间,用于检测节点是否失效。
这些元数据都存储在 Master 节点的内存中,并与其他节点进行同步,以确保集群中所有节点都拥有相同的元数据视图。通过 Master 节点维护这些元数据,可以实现集群管理和协调,确保数据的高可用性、一致性和完整性。 ↩︎