ElasticSearch知识点小记

ElasticSearch索引的基本操作

#创建索引
PUT /index_name
可以初始不定义
{
	"settings":{
		//索引设置
		"number_of_shards": "1",//索引的分片数决定了索引的并行度和数据分布 不可以动态修改
		"number_of_replicas": "1",//副本的数量提高了数据的可用性和容错能力 可以动态修改
	},
	"mappings":{
		//字段映射
		"properties": {
			"field1":{
				"type":"keyword"
			},
			"field2":{
				"type":"text",
				"analyzer":"ik_max_word",
				"search_analyzer":"ik_smart"
			},
		}
	}
}
#删除索引
DELETE /index_name

#查询索引
GET /index_name
GET  /index_name/_settings
GET /index_name/_mappings

#修改索引
PUT  /index_name/_settings
PUT /index_name/_mappings

#索引别名
#应用场景
1.在正在运行的集群上进行不同索引切换
2.多个索引分组组合
#创建索引时候指定别名
{
"aliases":{
	"alias_name":{}
}
"settings":{}
}
#现有索引添加别名
post /aliases
{
"actions":{
	"add":{
		"index":"index_name",
		"alias":"alias_name"
	}
}
}
#使用别名
访问Get alias_name

#多索引检索方案
逗号隔开
get index_name,index1_name/_search
通配符
get index_name_2024*/_search

ElasticSearch文档的基本操作

#新增文档
不指定id存在幂等性问题
post  /index/_create/id
{
"field":{}
}
#批量新增文档
支持在一次api调用中,对不同的索引进行操作
操作中单条操作失败,不会影响其他操作
返回结果中包括了每一条的执行结果
post /_bulk 
post /{index}/_bulk
post /{index}/{type}/_bulk

request
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_type" : "_doc", "_id" : "2" } }
{ "create" : { "_index" : "test", "_type" : "_doc", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_type" : "_doc", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }

response
{"took": 30,"errors": false,"items": [
//index 用于创建新文档或替换已有旧文档
{"index": {"_index": "test","_type": "_doc","_id": "1","_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"status": 201,"_seq_no" : 0,"_primary_term": 1}
},
//delete 用于删除现有文档
{"delete": {"_index": "test","_type": "_doc","_id": "2","_version": 1,"result": "not_found","_shards": {"total": 2,"successful": 1,"failed": 0},"status": 404,"_seq_no" : 1,"_primary_term" : 2}
},
//create 如果文档不存在则创建,如果文档已存在则返回错误
{"create": {"_index": "test","_type": "_doc","_id": "3","_version": 1,"result": "created","_shards": {"total": 2,"successful": 1,"failed": 0},"status": 201,"_seq_no" : 2,"_primary_term" : 3}
},
//用于更新现有文档
{"update": {"_index": "test","_type": "_doc","_id": "1","_version": 2,"result": "updated","_shards": {"total": 2,"successful": 1,"failed": 0},"status": 200,"_seq_no" : 3,"_primary_term" : 4}
}
]
}
#查询分词结果
post _analyze
{
	"analyzer":"ik_max_word",
	"text":"value"
}
#查询文档
根据id查询
get /index_name/_doc/id
同时查询多个id
get index_name/_mget
{
  "ids":[id1,id2...]
}
查询文档数量
get /index_name/_count
条件匹配文档
GET /index_name/_search
{
	"query":{
		//匹配所有文档
		"match_all":{},
		//文本字段匹配(全文检索)
		"match":{
			"field_name":"value"
		},
		//精确匹配
		"term":{
			"field_name":"value"
		},
		//范围查询
		"range":{
			"field_name":{
				"gte":"lower_bound",
				"lte":"upper_bound"
			}
		}
	}
}
#删除文档
单个删除
delete /index_name/_doc/id
批量删除
post _bulk
{"delete":{"_index":"index_name","_id":"value"}}
{"delete":{"_index":"index_name","_id":"value1"}}
条件查询删除
post /index_name/_delete_by_query
{
	"query":{
		"<your_query>"
	}
}
#更新文档
单个更新
post index/_update/1
{
	"doc":{
		"field":"value"
	}
}
并发控制
post /index_name/_doc/id?if_seq_no=1&if_primary_term=3

批量更新
post _bulk
{ "update" : {"_id" : "1", "_type" : "_doc", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
post /index_name/_update_by_query
{
	"query":{
		"<your_query>"
	},
	"script":{
		"source":"ctx._source.field=value"
	}
}

Elasticsearch文档建模最佳实践

#嵌套对象(Nested Object)
适用于对少量,子文档偶尔更新,查询频繁的场景
允许对象数组中的对象被独立索引
在内部,Nested文档会被保存在两个Lucene文档中,在查询时做join处理

#Join父子文档类型
适用子文档更新频繁
Join类型用在同一索引的文档中创建父子关系
父文档和子文档是独立的两个文档
父文档和子文档必须在相同的分片上,能够确保查询join的性能
当指定子文档时,必须指定他的父文档id,使用routing参数来保证,分配到相同的分片上

#宽表冗余存储
宽表适用于一对多或者多对多的关联关系
#业务端关联
适用于数据量少的多表关联业务场景
#最佳实践
##如何处理关联关系
object:优先考虑反范式
Nested:当数据包含多数值对象,同时有查询需求
Child/parent:关联文档更新非常频繁时

##避免过多字段
一个文档中,避免大量的字段
过多的字段不容易维护
Mapping信息不保存在Cluster State中,数据量过大,对集群性能会有影响
删除或者修改数据需要reindex
post _reindex
{
	"source":{
		"index":"{sourceIndex}"
	},
	"dest":{
		"index":"{destIndex}"
	}
}
默认最大字段数是1000,可以通过index.mapping.total_fields.limit规定最大字段数

生产环境中,尽量在mapping的dynamic字段中配置strict严格控制新增字段的加入(设置为true时,未知字段会被自动加入;设置为false时,新字段不会被索引,但会保存在_source)

对于多属性的字段,可以考虑适用Nested


避免正则、通配符、前缀查询
属于term查询,性能不够好

避免空值引起的聚合不准
mapping时添加配置字段的属性null_value:0

为索引的mapping加入meta信息
功能:搜索、聚合、排序
性能:存储、内存、搜索的性能
mapping添加_meta属性的index_version_mapping的字段


ElasticSearch复杂查询

分页查询

https://zhuanlan.zhihu.com/p/344769338

浅分页 from+size

如果数据量小(from+size 在 10000 条内),或者只关注结果集的 TopN 数据,可以使用 from/size 分页,简单粗暴

GET /index/_search
{
  "from": 10,
  "size": 20
}

深分页 scroll(使用场景 数据导出)

数据量大,深度翻页,后台批处理任务(数据迁移)之类的任务,使用 scroll 方式

GET http://localhost:9200/my_index/_search?scroll=1m
{  
  "size": 100,   
  "query": {  
    "match_all": {}  
  }  
}  
res将得到一个_scroll_id
下一次查询(依此逻辑多次重复查询) 
GET http://localhost:9200/_search/scroll  
{  
  "scroll": "1m",  
  "scroll_id": "YOUR_SCROLL_ID"  
} 
1m的含义为表示设置scroll_id保留1分钟可用
使用scroll必须要将from设置为0

完成查询后,您可能需要清理 Scroll 上下文(可选)
DELETE http://localhost:9200/_search/scroll
{  
  "scroll_id": ["YOUR_SCROLL_ID1", "YOUR_SCROLL_ID2", ...]  
}  

深分页 search_after(不支持跳页查询)

数据量大,深度翻页,用户实时、高并发查询需求,使用 search after 方式

GET http://localhost:9200/my_index/_search
{  
  "query": {  
    "match_all": {}  
  },  
  "size": 10,  
  "sort": [  
    { "timestamp": "asc" },  
    { "_id": "asc" }  // 为了确保排序唯一  
  ]  
}  
res获取最后一条记录的 timestamp 和 _id 值,这些值将用于后续的 search_after 查询
下一步执行查询(重复获取上一条记录,重复执行)
GET http://localhost:9200/my_index/_search
{  
  "query": {  
    "match_all": {}  
  },  
  "size": 10,  
  "sort": [  
    { "timestamp": "asc" },  
    { "_id": "asc" }  
  ],  
  "search_after": [ "2024-01-01T00:00:00", "abc123" ]  
}  
使用search_after必须要设置from=0。
这里使用timestamp和_id作为唯一值排序。
在返回的最后一条数据里拿到sort属性的值传入到search_after。

match_all匹配所有文档

get /index_name/_search
{
	"query":{
		"match_all":{}
	},
	"sort":{
		"_score":{
			"order":"desc"
		}
	},//指定根据分数排序
	"_source":false,//不查看元数据,仅查看原字段 或者指定具体显示字段数组["field1","field2"]
	"from":0,//分页起始行
	"size":10//默认size=10
}

精确匹配

精确匹配指的是文本内容不经过分析直接用于文本匹配,搜索的对象大多是非text类型的字段,主要应用于结构化数据

term单字段精确匹配查询

避免将term检索应用于text类型的字段

get /{index_name}/_search
{
	"query":{
		"term":{
			"{field_name.keyword}":{
				"value":"{expected_value}"
			}
		}
	}
}
//可以通过Constant Score将查询转换成一个filtering,避免算分,并利用缓存,提高性能
将query转换成filter,忽略tf-idf计算,避免相关性算分的开销
filter可以有效利用缓存
get /{index_name}/_search
{
	"query":{
		"constant_score":{
			"filter":{
				"term":{
					"{field_name.keyword}":"{expected_value}"
				}
			}
		}
	}
}

terms多字段精确匹配查询

允许用户在单个查询中指定多个词条来进行精确匹配

get /{index_name}/_search
{
	"query":{
		"terms":{
			"{field_name.keyword}":[
				//or匹配
				"{expected_value}","{expected1_value}",...
			]
		}
	}
}

range范围查询

对数字、日期、其他可排序数据类型的字段进行范围筛选
支持大于gt 大于等于gte 小于lt 小于等于lte

get /{index_name}/_search
{
	"query":{
		"range":{
			"{field_name}":{
				"gte":"{lower_bound}",
				"lte":"{upper_bound}",
				"gt":"{greater_than_bound}",
				"lt":"{less_than_bound}"
			}
		}
	}
}

exists是否存在查询

适用于检查文档中是否包含某个字段,或者该字段是否包含非空值

get /{index_name}/_search
{
	"query":{
		"exists":{
			"{field}":"{missing_field}"
		}
	}
}

ids根据一组id查询

get /{index_name}/_search
{
	"query":{
		"ids":{
			"values":[
				//or匹配
				"{id1}","{id2}",...
			]
		}
	}
}

prefix前缀匹配

prefix会对分词后的term进行前缀搜索
它不会对要搜索的字符串进行分词,传入的前缀就是想要查找的前缀
默认状态下,前缀查询不做相关分数计算,它只是将所有匹配的文档返回,然后赋予所有相关分数值为1
用于自动补全或搜索功能

get /{index_name}/_search
{
	"query":{
		"prefix":{
			"{field_name.keyword}":{
				"value":"{expected_value}"
			}
		}
	}
}

wildcard通配符匹配

允许在检索中使用通配符表达式来匹配文档的字段值
星号(*):表示0和多个字符,可用于匹配任意长度的字符串
问号(?):表示一个字符,用于匹配任意单个字符
通配符可能会导致大量的计算负担

get /{index_name}/_search
{
	"query":{
		"wildcard":{
			"{field_name.keyword}":{
				"value":"{search_pattern}"
			}
		}
	}
}

fuzzy支持编辑距离的模糊查询

能够在用户输入内容存在拼写错误或上下文不一致时,仍然返回与搜索词相似的文档。
编辑距离是指一个单词转换到另一个单词需要编辑单字符的次数。如中文集团和中为集团编辑距离就是1,只需要修改一个字符。如果fuzziness的值为2,这里会把编辑距离为2的东东集团查出来

get /{index_name}/_search
{
	"query":{
		"fuzzy":{
			"{field_name}":{
				"value":"{search_term}",
				"fuzziness":"AUTO",//或者设置具体编辑距离1
				"prefix_length": 1
			}
		}
	}
}

regexp正则匹配查询

非必要情况下不宜使用
比wildcard查询更加灵活和强大,使用正则表达式匹配

get /{index_name}/_search
{
	"query":{
		"regexp":{
			"{field_name.keyword}":{
				"value":"{search_pattern}"
			}
		}
	}
}

term set用于解决多值字段中的文档匹配问题

在处理具有多个属性,分类或标签的复杂数据时非常有用
适用于标签系统,搜索引擎,电子商务系统,文档管理系统和技能匹配等场景

get /{index_name}/_search
{
	"query":{
		"terms_set":{
			"{field_name}":{
				"terms":["{term1}","{term2}",...],
				"minimum_should_match_field":"{minimum_should_match_field_name}"//或者
				"minimum_should_match_script":{
					"source":"<script>"
				}
			}
		}
	}
}
//至少匹配数量 
"minimum_should_match_field":2
或
"minimum_should_match_script":{
					"source":2
				}
//动态计算的标签匹配数量
"minimum_should_match_field":"tags_count""minimum_should_match_script":{
					"source":"doc['tags_count'].value*0.7"
				}

全文检索

会对输入的文本进行分析,将其拆分为词项(单个单词),并执行诸如分词、词干处理、标准化等操作。主要用于非结构化文本数据

match分词查询

1.分词
输入的查询文本会被分词器进行分词,分词器会将文本拆分成一个个词项(terms),如单词、短语或特定字符。分词器通常根据特定的语言规则和配置进行操作。
2.匹配计算
一旦查询被分词,es将根据查询的类型和参数计算文档与查询的匹配度。对于match查询,es将比较查询的词项与倒排索引中的词项,并计算文档的相关性得分。相关性得分衡量了文档与查询的匹配程度。
3.结果返回
根据相关性得分,es将返回最匹配的文档作为搜索结果。搜索结果通常按照相关性进行排序,以便最相关的文档排在前面。

get /index_name/_search
{
	"query":{
		"match":{
			//默认是or
			"{field_name}":"{match_value}"
			//可以换一种为and查询
			"{field_name}":{
				"query":"{match_value}",
				"operator":"and"
				//指定至少匹配的词
				"mininum_should_match": 2
			}
		}
	}
}

multi_match多字段查询

可以接受一个字符串,并在指定的字段集合中搜索这个字符串

get /index_name/_search
{
	"query":{
		"multi_match":{
			"query":"{match_value}",
			"fields":["field1","field2",...]
		}
	}
}

match_phrase短语查询

不仅匹配整个短语,还考虑了短语中各个词的顺序和位置。
适用严格匹配的场景
根据分词的position确定分词的顺序

get /index_name/_search
{
	"query":{
		"match_phrase":{
			"{field_name}":{
				"query":"{match_phrase_value}",
				//slop为0意味着短语中的词必须按照顺序出现,非0允许错位
				"slop":0
			}
		}
	}
}

query_string支持与或非表达式的查询

支持多种逻辑运算符,与and、或or、非not,以及通配符、模糊搜索和正则表达式等功能。
可以在单个或多个字段上进行搜索,并且可以处理复杂的查询逻辑。
应用场景包括高级搜索、数据分析和报表等

get /index_name/_search
{
	"query":{
		"query_string":{
			//query_string时可以使用表达式
			"query":"{query_string}",
			//或者使用fields "fields":["{field_name}","{field1_name}",...]
			"default_field":"{field_name}"
		}
	}
}

simple_query_string

会忽略query_string的错误表达式匹配
+替代and
|替代or
-替代not
生产推荐,容忍语法错误

get /index_name/_search
{
	"query":{
		"simple_query_string":{
			//query_string时可以使用表达式
			"query":"{query_string}",
			"fields":["{field_name}","{field1_name}",...],
			//可以忽略
			"default_operator":"and"//或者or
		}
	}
}

组合查询

bool query查询(多条件组合查询)

布尔查询可以按照布尔逻辑条件组织多条查询语句,只有符合整个布尔条件的文档才会被搜索出来
在布尔条件中,可以包含两种不同的上下文
1.搜索上下文(query context):使用搜索上下文时,Es需要计算每个文档与搜索条件的相关性得分,这个得分的计算需要一套复杂的算法,有一定的性能损耗,带文本分析的全文检索的查询语句很适合放在搜索上下文中。
2.过滤上下文(filter context):使用过滤上下文时,Es只需要判断搜索条件跟文档数据是否匹配。比如term、range等。过滤上下文的查询不需要相关度得分计算,还可以使用缓存加速响应速度,很多术语级查询语句都适合放在过滤上下文中。
布尔查询一共支持四种组合类型:
must 可包含多个查询条件,每个条件均满足的文档才会被搜索到,每次查询需要计算相关度得分,属于搜索上下文。
should 可包含多个查询条件,不存在must和filter条件时,至少要满足多个查询条件中的一个,文档才能被搜索到,否则需满足的条件数量不受限制,匹配到的查询越多相关度越高,也属于搜索上下文。
filter 可包含多个过滤条件,每个条件均满足的文档才能被搜索到,每个过滤条件不计算相关度得分,结果在一定条件下被缓存,属于过滤上下文。
must_not 可包含多个过滤条件,每个条件均不满足的文档才能被搜索到,每个过滤条件不计算相关度得分,结果在一定条件下会被缓存,属于过滤上下文。

get /index_name/_search
{
	"query":{
		"bool":{
			"must":[
				{"match":{
					"{field_name}":"{value}"
				}},
				{"match":{
					"{field1_name}":"{value1}"
				}}
			]
		}
		
	}
}

get /index_name/_search
{
	"query":{
		"bool":{
			"should":[
				{"match":{
					"{field_name}":"{value}"
				}},
				{"match":{
					"{field1_name}":"{value1}"
				}}
			],
			//至少一个满足
			"mininum_should_match":1
		}
		
	}
}



get /index_name/_search
{
	"query":{
		"bool":{
			"filter":[
				{"term":{
					"{field_name}":"{value}"
				}},
				{"range":{
					"{field1_name}":"{value1}"
				}}
			]
		}
		
	}
}

highlight高亮显示实现

highlight关键字,可以让符合条件的文档中的关键词高亮
highlight相关属性:
pre_tags:前缀标签
post_tags:后缀标签
tags_schema:设置为styled可以使用内置高亮样式
require_field_match:多字段高亮需要设置为false

get /{index_name}/_search
{
	"query":{
		"term":{
			"{field_name.keyword}":{
				"value":"{expected_value}"
			}
		}
	},
	"highlight":{
		"pre_tags":["</span>"],
		"post_tags:["<span style='color:red'>"],
		"required_field_match":false,
		"fields":{
			"*":{}//只对查询字段高亮
			//或者具体指定字段
			"fieldName":{}
		} 
	}
}

地理空间位置查询

主要应用于地理信息系统(GIS)和位置服务当中
允许用户基于地理位置信息来搜索和过滤数据
地理空间位置通常存储在geo_point字段类型中。这种字段类型通常可以存储纬度和经度坐标,用于表示地球上的一个点。

put /index_name
{
	"mappings":{
		"properties":{
			"location":{
				"type":"geo_point"
			}
		}
	}
}

使用以下查询来找到距离给定坐标点(例如lat和lon)小于或等于10公里的所有文档
get /index_name/_search
{
	"query":{
		"bool":{
			"must":{
				"match_all":{}
			}
		},
		"filter":{
			"geo_distance":{
				"distance":"10km",
				"distance_type":"rac",
				"location":{
					"lat":39.9,
					"lon":116.4
				}
			}
		}
	}
}
bool:是一个逻辑查询容器,用于组合多个查询子句。
match_all:是一个匹配所有文档的子句。
geo_distance:是一个地理位置查询,它允许您指定一个距离和一个点的坐标
distance:查询的最大距离,单位可以是(米)m、(公里)km
distance_type:可以是arc(以地球表面的弧长为单位,圆弧距离)或plane(以直线距离为单位)。通常对于地球上的距离查询,建议使用arc
distance:是查询的基准点,包含纬度和经度坐标。

8.X向量检索

通过KNN算法支持向量近邻检索。
向量检索的基本思路是,将文档或是数据项表现为高级向量,并使用这些向量来执行相似性搜索。在es中,这些向量被存储在dense_vector类型的字段中,然后使用KNN算法来找到与给定向量最相似的其他变量。

put /index_name
{
	"mappings":{
		"properties":{
			"image-vector":{
				"type":"dense_vector",
				//维度 3维
				"dims":3
			},
			...
		}
	}
}

post /index_name/_search
{
	"knn":{
		"field":"{field_name}",
		"query_vector":[],//指定向量
		"k":10,//查询的数据条数
		"num_candidates":100
	},
	"fields":["title","field_type",...]
}

搜索相关性评分详细

相关性的概述

#什么是相关性
在搜索引擎中,描述一个文档与查询语句匹配程度的度量标准
_score是ElasticSearch检索返回的评分,文档评分越高,则该文档的相关性越高。
#计算相关性评分
在es5之前,评分机制基于TF-IDF实现;在es5之后,默认的打分机制变更为Okapi BM25
##TF-IDF算法
TF是词频:检索词在文档中出现的次数越高,相关性越高。
IDF是逆向文本频率:每个检索词在索引中出现的频率,频率越高,相关性越低。
字段长度归一值:检索词出现在一个内容短的title要比同样的词出现在一个内容长的content字段权重更大。
boosting提升:可以指定字段算分提升的权重
##BM25算法
和tf-idf算法相比,当tf无限增加时,bm25算分会趋于一个数值。
#通过Explain的api查看算分的过程
get /index_name/_explain/{id}
{
	"query":{
		"match":{
			"{field_name}":"{field_value}"
		}
	}
}

自定义评分的策略

自定义评分的核心是通过修改评分来修改文档相关性,在最前面的位置返回用户最期望的结果。

#index boost 在索引层面修改相关性
在跨多个索引搜索时为每个索引配置不同的级别。适用于索引级别调整评分
借助indices_boost提升索引的权重
get index_name*/_search
{
	"query":{
		"term":{
			"{field_name.keyword}":{
				"value":"{search_field_value}"
			}
		}
	},
	"indices_boost":[
		{
			"{index_name1}":2
		},
		{
			"{index_name2}":1
		},
		...
	]
}
#boosting 修改文档相关性
可在查询时修改文档的相关性,修改不同字段的算分比重
boost值所在范围不同,含义不同
若boosting值为0~1,则表示降低评分
若boosting值为>1,则表示提升评分
get /index_name/_search
{
	"query":{
		"bool":{
			"should":[
				{
					"match":{
						"{field_name}":{
							"query":"{query_value}",
							"boost":4
						}
					}
				},
				{
					"match":{
						"{field_name}":{
							"query":"{query_value}",
							"boost":1
						}
					}
				}
			]
		}
	}
}
#negative_boost 降低相关性
若对某些结果不满意,但又不想将其排除(must_not),则可以考虑采用negative_boost方式
原理说明如下:
negative_boost仅对查询中定义为negative的部分生效
计算评分时,不修改boosting部分评分,而negative部分的评分则乘以negative_boost的值
negative_boost取值为0-1
要求某个具体数据的信息优先展示
get /index_name/_search
{
	"query":{
		"boosting":{
			"positive":{
				"match":{
					"{field_name}:"{field_value}"
				}
			},
			"negative":{
				"match":{
					"{field_name}:"{field_value}"
				}
			},
			"negative_boost":0.2
		}
	}
}
#function_score 自定义评分
支持用户自定义一个或多个查询语句及脚本,达到精细化控制评分的目的,适用于需进行复杂查询的自定义评分业务场景。
可以借助script_score实现

get /index_name/_search
{
	"query":{
		"function_score":{
			"query":{
				"match_all":{}
			},
			"script_score":{
				"script":{
				//只是一个脚本示例,使用原始分数字段乘文档目标字段的和
					"source":"_score*(doc['field_name1'].value+doc['field_name'].value+...)"
				}
			}
		}
	}
}
#rescore_query 查询后二次打分
二次评分是指重新计算查询返回的结果文档中指定文档的得分
es会截取查询返回的前N条结果,并使用预定义的二次评分方法来重新计算其得分。但对全部有序的结果集进行重新排序的话,开销会比较大,使用rescore可以只对结果集的子集进行处理。该方式适用于对查询语句的结果不满意,需要重新打分的场景。
get /index_name/_search
{
	"query":{
		"match":{
			"{field_name}":"{field_value}"
		}
	},
	"rescore":{
		"query":{
			"rescore_query":{
				"match":{
					"{field_name}":"{field_value}"
				}
			},
			//原先查询的权重
			"query_weight":0.7,
			//为本次查询二次打分增加权重
			"rescore_query_weight":1.2
		},
		//表示取分片结果的前50进行重新算分
		"window_size":50
	}
}

多字段搜索场景优化

#最佳字段
多个字段中返回评分最高的
get /index_name/_search
{
	//自定义添加explain用于查询执行计划
	"explain":true,
	"query":{
		"bool":{
			"should":[
				{
					"match":{
						"{field_name}":"{field_value}"
					}
				},
				{
					"match":{
						"{another_field_name}":"{field_value}"
					}
				}
			]
		}
	}
}
bool should查询是简单的评分相加,应使用dis_max寻找最佳匹配

get /index_name/_search
{
	//自定义添加explain用于查询执行计划
	"explain":true,
	"query":{
		"dis_max":{
			"queries":[
				{
					"match":{
						"{field_name}":"{field_value}"
					}
				},
				{
					"match":{
						"{another_field_name}":"{field_value}"
					}
				}
			],
			//tie_breaker是一个介于0-1之间的浮点数,0代表最佳匹配 ;1代表所有语句同样重要
			//最终得分=最佳匹配字段+其他匹配字段*tie_breaker
			"tie_breaker":0.1
		}
	}
}
best_fields查询
获取最佳匹配字段的得分,final_score=max(其他匹配字段得分,最佳匹配字段得分)
采用best_fields查询,并添加参数tie_breaker=0.1,final_score=其他匹配字段得分*0.1+最佳匹配字段得分
best_fields是默认类型,可以不用指定,等价于dis_max查询方式
get /index_name/_search
{
	"query":{
		"multi_match":{
			//默认 best_fields
			"type":"best_fields",
			"query":"{query_value}",
			fields:["{field_name1}","{field_name2}",...],
			"tie_breaker":0.2
		}
	}
}
#多数字段
匹配多个字段,返回各个字段评分之和
most_fields策略获取全部匹配字段的累计得分(综合全部匹配字段的得分),等价于bool should的查询方式(求和得分计算)
get /index_name/_search
{
	"query":{
		"multi_match":{
			//默认 best_fields
			"type":"most_fields",
			"query":"{query_value}",
			//.std为指定字段的子字段 可以用来区分不同的查询分词器
			//^10为提高该字段的boost值的比重,使该字段在查询计分中更为重要
			fields:["{field_name^10}","{field_name1.std}","{field_name2}",...],
			"tie_breaker":0.2
		}
	}
}
#混合字段 跨字段搜索
跨字段匹配,待查询内容在多个字段中都有显示,类似bool+dis_max的组合
get /index_name/_search
{
	"query":{
		"multi_match":{
			//默认 best_fields
			"type":"cross_fields",
			"query":"{query_value}",
			fields:["{field_name1}","{field_name2}",...],
			"operator":"and"
		}
	}
}
还可以使用copy_to解决,但是需要额外的存储空间
在建立mapping字段时,给字段添加copy_to属性指定到具体的字段,例如两个字段都是存储到{full}
get /index_name/_search
{
	"query":{
		"match":{
			//full_field_name为在mapping时存储的copy_to属性的字段
			"{full_field_name}":{
				"query":"{search_value}",
				"operator":"and"
			}
		}
	}
}

ES聚合操作

聚合的概述

实现对数据的统计、分析、运算。
应用场景:
电商平台的销售分析、社交平台的用户行为分析、物流企业的运输分析、金融企业的交易分析、智能家居的设备监控分析

聚合的分类

#指标聚合
单值分析:只输出一个分析结果
min、max、avg、sum
get /index_name/_search
{
	"size":0,
	"aggs":{
		"{agg_fieldName}":{
			"max":{
				"field":"{field_name}"
			}
		},
		"{agg_fieldName}":{
			"min":{
				"field":"{field_name}"
			}
		},
		"{agg_fieldName}":{
			"avg":{
				"field":"{field_name}"
			}
		}
	}
}
res 返回
{
	...
	,
	"aggregations":{
		"agg_fieldName":{
			"value":"aggValue"
		}
	}
}
Cardinality(类似distict count)
get /index_name/_search
{
	"size":0,
	"aggs":{
		"{agg_fieldName}":{
			//对搜索结果去重 aggs返回去重后的量值
			"cardinality":{
				"field":"{field_name}"
			}
		}
	}
}
多值分析:输出多个分析结果
stats(统计)、extend stats
get /index_name/_search
{
	"size":0,
	"aggs":{
		"{agg_fieldName}":{
			//一个聚合输出多个统计
			"stats":{
				"field":"{field_name}"
			}
		}
	}
}
percentile(百分位)、percentile rank
top hits (排在前面的示例)
#桶聚合
按照一定的规则,将文档分配到不同的桶中,从而达到分类的目的。es提供一些常见的Bucket Aggregation(类似group by)
Terms,需要字段支持fileddata
	keyword默认支持fileddata
	text需要在mapping中开启fielddata(即设置fielddata为true),会按照分词后的结果进行分桶
	对keyword以及text进行聚合,分桶的总数并不一样
get /index_name/_search
{
	//限定聚合的范围
	"query":{
		"range":{
			"fieldName":{
				"gte":"value"
			}
		}
	},
	"size":0,
	"aggs":{
		"{agg_fieldName}":{
			//一个聚合输出多个统计
			"terms":{
				//指定聚合结果字段
				"field":"{field_name.keyword}",
				//指定聚合结果数量
				"size":10,
				//order 指定聚合结果排序方式
				"order":{
					//_count为统计bucket内的文档数量
					"_count":"desc"
				}
			}
		}
	}
}
res 返回
{
	...
	,
	"aggregations":{
		"agg_fieldName":{
			"buckets":[
				{
					"key":"bucketKey",
					"doc_count":"count_value"
				}
			]
		}
	}
}
数字类型
按照数字的范围进行分桶
在Range Aggregation中,可以自定义key
	Range/Data Range
	Histogram(直方图)/Date Histogram
get /index_name/_search
{
	"size":0,
	"aggs":{
		"{agg_fieldName}":{
			"range":{
				//指定聚合结果字段
				"field":"{field_name}",
				//指定不同分组范围
				"ranges":[
					{
						"to":"{toValue}"
					},
					{
						"from":"{toValue}"
						"to":"{toValue}"
					},
					{
						//例>20000表示大于20000的数据为一组
						"key":"{keyPatternValue}"
						"from":"{toValue}"
					}
				]
			}
		},
		"{agg_fieldName1}":{
			"histogram":{
				//指定聚合结果字段
				"field":"{field_name}",
				//指定间隔
				"interval":"{intervalValue}",
				//指定区间
				"extended_bounds":{
					"min":"{minValue}",
					"max":"{maxValue}"
				}
			}
		}
	}
}
支持嵌套:在桶里在做分桶
先分桶再topHits
get /index_name/_search
{
	"query":{
		"range":{
			"fieldName":{
				"gte":"value"
			}
		}
	},
	"size":0,
	"aggs":{
		"{agg_fieldName}":{
			"terms":{
				"field":"{field_name.keyword}"
			},
			"aggs":{
				"{agg_fieldName}":{
					//当获取分桶后,桶内最匹配的文档列表
					"top_hits":{
						"size":3,
						"sort":[
							{
								"{fieldName}":{
									"order":"desc"
								}
							}
						]
					}
				}
			}
		}
	}
}
先分桶再stat
先分桶再stat再stat
#管道聚合
支持对聚合分析的结果再次进行聚合分析。Pipeline的分析结果会输出到原结果中,根据位置的不同,分为两类:
Sibling-结果和现有结果分析同级
	Max、Min、avg&sum bucket
get /index_name/_search
{
	"size":0,
	"aggs":{
		"jobs":{
			//分组
			"terms":{
				"field":"{fieldName}",
				"size":10
			},
			//嵌套获取平均数据
			"aggs":{
				//>aggsName
				"{aggsName}":{
					"avg":{
						"field":"{fieldName}"
					}
				}
			}
		},
		//依据桶的路径求最小的桶的聚合统计结果 
		"{bucketName}":{
			"min_bucket":{
				"bucket_path":"jobs>aggsName"
			}
		}
	}
}
bucketName结果和jobs的聚合同级
min_bucket求之前结果的最小值
通过bucket_path关键字指定路径
	stats,Extended status bucket
get /index_name/_search
{
	"size":0,
	"aggs":{
		"jobs":{
			//分组
			"terms":{
				"field":"{fieldName}",
				"size":10
			},
			//嵌套获取平均数据
			"aggs":{
				//>aggsName
				"{aggsName}":{
					"avg":{
						"field":"{fieldName}"
					}
				}
			}
		},
		//依据桶的路径求桶的聚合统计结果 
		"{bucketName}":{
			"stats_bucket":{
				"bucket_path":"jobs>aggsName"
			}
		}
	}
}
	Percentiles bucket
get /index_name/_search
{
	"size":0,
	"aggs":{
		"jobs":{
			//分组
			"terms":{
				"field":"{fieldName}",
				"size":10
			},
			//嵌套获取平均数据
			"aggs":{
				//>aggsName
				"{aggsName}":{
					"avg":{
						"field":"{fieldName}"
					}
				}
			}
		},
		//依据桶的路径求桶的百分位数聚合统计结果 
		"{bucketName}":{
			"percentiles_bucket":{
				"bucket_path":"jobs>aggsName"
			}
		}
	}
}
Parent-结果内嵌到现有的聚合分析结果之中
	Derivative(求导)
	Cumultive Sum(累计求和)
get /index_name/_search
{
	"size":0,
	"aggs":{
		"{aggName}":{
			//对字段做区间统计
			"histogram":{
				"field":"{fieldName}",
				"min_doc_count":0,
				"interval":1
			},
			"aggs":{
				"{aggName1}":{
					"avg":{
						"field":"{fieldName}"
					}
				},
				"{cumulative_name}":{
					//累计求和汇总上面aggName1
					"cumulative_sum":{
						"buckets_path":"aggName1"
					}
				}
			}
		}
	}
}
	Moving Function(移动平均值)


ES聚合分析不精准原因

数据量、实时性、精确度只能满足两种
数据量-实时性(近似计算)(es采用的方式)
精确度-数据量(离线计算)
精确度-实时性(有限数据计算)

多个节点 聚合实时  每个节点取top3再聚合top3不精准

ES聚合分析不精准解决方案

#设置主分片为1
es7版本已经默认为1
适用场景,数据量少的小集群规模业务场景
#调大shard_size值
官方推荐size*1.5+10.shard_size值越大,结果越趋近于精准聚合结果值。此外,还可以通过show_term_doc_count_error参数显示最差情况下的错误值,用于辅助确定shard_size大小。
size:是聚合结果的返回值,客户期望聚合返回排名前三,size为3
shard_size:每个分片上聚合的数据条数。shard_size原则上要大于等于size
适用场景:数据量大,分片数多的集群业务场景。
get /index_name/_search
{
	"size":0,
	"aggs":{
		"{aggname}":{
			"term":{
				"field":"{fieldName}",
				//总共取5条
				"size":5,
				//每个分片取10条
				"shard_size":10,
				"show_term_doc_count_error":true
			}
		}
	}
}
res
doc_count_error_upper_bound:被遗漏的term分桶,包含的文档,有可能的最大值。
sum_other_doc_count:除了返回结果bucket的terms以外,其他terms的文档总数(总数-返回的总数)

#将size设置为全量值,解决精度问题
将size设置为232次方减去1也就是分片的最大值,来解决精度问题。
原因:1.x版本,size等于0代表全部,高版本取消0值,所以设置了最大值(大于业务的全量值)。
全量带来的弊端就是:如果分片数据量极大,这样做会耗费巨大的cpu资源来排序,而且可能会阻塞网络
适用场景:对聚合精度要求极高的业务场景,由于性能问题,不推荐使用。

#使用clickhouse/spark进行精准聚合
适用场景:数据量非常大,聚合精度要求高、响应速度快的业务场景。

聚合性能优化

#插入数据时对索引进行预排序
index sorting(索引排序)可用于在插入时对索引进行预排序,而不是查询时再对索引进行排序,这将提高范围查询(range query)和排序操作的性能。
在es创建索引时,可以配置如何对每个分片内的字段进行排序
这是es6之后才有的特性。
put /index_name
{
	"settings":{
		"index":{
			"sort.field":"",
			"sort.order":"desc"
		}
	},
	"mappings":{
		"properties":{
			"field":{
				"type":"date"
			}
		}
	}
}
注意:预排序将增加elasticsearch写入的成本。在某些用户场景下,开启索引预排序会导致大约40%-50%的写性能下降。也就是说,如果用户场景更关注写性能的业务,开启索引预排序不是一个很好的选择。
#适用节点查询缓存
filter查询
节点查询缓存可用于有效缓存过滤器操作的结果。如果多次执行同一filter操作,这将很有效,但是即便更改过滤器中某一个值,也将意味着需要计算新的过滤器结果。
get /index_name/_search
{
	"query":{
		"bool":{
			"filter":{
				"term":{
					"your_field":"your_value"
				}
			}
		}
	}
}
#适用分片请求缓存
聚合语句中,设置size为0,就会使用分片请求缓存结果。size=0的含义是:只返回聚合结果,不返回查询结果
get /index_name/_search
{
	"size":0,
	"aggs":{
		"aggName":{
			"terms":{
				"field":"fieldName"
			}
		}
	}
}
#拆分聚合,使聚合并行化
es查询条件同时有多个条件聚合,默认情况下聚合不是并行运行的。当为每个聚合提供自己的查询并执行msearch时,性能会有显著提升。因此,在cpu资源不是瓶颈的情况下,如果想缩短响应时间,可以将多个聚合拆分为多个查询,借助msearch实现并行聚合。
常规的多条件聚合实现
get /index_name/_search
{
	"size":0,
	"aggs":{
		"aggsName":{
			"terms":{
				"field":""
			}
		},
		"aggsName1":{
			"max":{
				"field":""
			}
		}
	}
}
使用msearch拆分多个语句的聚合实现
get _msearch
{"index":"index_name"}
{"size":0,"aggs":{"aggsName":{"terms":{"field":"field"}}}}
{"index":"index_name"}
{"size":0,"aggs":{"aggsName":{"max":{"field":"field1"}}}}

ES调优

极致 ElasticSearch 调优,让你的ES 狂飙100倍!

问题

Can't update non dynamic settings [[index.analysis.filter

# 关闭索引
POST index/_close

# 设置分词器
PUT index/_settings
{"index": {
               
            }
}

# 开启索引
POST index/_open

posted @   梦回大唐meng  阅读(29)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示