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设置为2的32次方减去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
本文来自博客园,作者:梦回大唐meng,转载请注明原文链接:https://www.cnblogs.com/BitX/p/18513097
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步