es的官方中文文档
https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
概念
es是基于Lucene,纯java语言编写的分布式,可扩展,近实时性的高性能搜索引擎和数据分析引擎
es基础构成
-
doc
es中数据存储的最小单元,对应数据库的一行数据 -
Index
有具有相同字段的文档列表组成,对应数据库的一个表,一个索引由一个或者多个分片组成 -
type
文档类型,这个我们可以不用记,es后面的版本要把type去掉。之前的版本中,索引和文档中间还有个类型的概念,每个索引下可以建立多个类型,文档存储时需要指定index和type。从6.0.0开始单个索引中只能有一个类型,7.0.0以后将将不建议使用,8.0.0 以后完全不支持。在7.0开始,一个索引只能建一个Type为_doc。 -
shards
分片。当索引数据太大时,受限于单个节点的磁盘,内存,处理能力等资源,需要将索引的数据水平拆分,拆分后形成的每一个分片回落到不一样的服务节点上,这样显著提升了es的性能,索引创建时需要指定分片,一旦分片被指定就无法更改 -
replicas
副本。是主分片的备份分片,副本主要是用于主分片的数据备份,同时也对外提供数据查询服务。副本分片提升了es的高可用性
分片shard和es集群的高可用高性能
分片的概念和kafka的分区很像,既有副本又各自拥有不同的数据并且分散在不同的节点,index就是这些分片的topic。
一个索引中的数据保存通过hash取余决定保存在哪分片中,相当于水平分表也类似于kafka的分区,分片有主分片和副本分片,一个主分片可以设置多个副本分片,在es集群中每个主分片存储了es的部分数据,副本分片主要是用于备份主分片的数据,主分片分布在es的各个节点上,而各个主分片的副本分片也会分布到和主分片不同的节点上,当集群中某个节点宕机时,分布在其他节点的副本分片就会顶替原来的主分片,这个体现了es的高可用性。
由于分片分散在各个不同的节点上,每个节点都有能力处理任意请求, 每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上,所以其请求压力和数据分析压力也会分散到集群的各个节点上,实现了es的高性能。我们最好通过轮询的方式访问集群的节点
分片的原理就和数据库的水平分表和kafka的分区是一个道理的,既能保证高新南
下图的流程展示es集群的高可用
图一:es集群中有三个节点,三个主分片,三个副本分片,也就是每个分片拥有一个副本分片
图二:主节点宕机,node2通过选举称为新主节点
图三:挂掉的node1的副本分片存储在node3上,集群将副本分片P0调整为主分片,这样node1的数据就恢复回来了
图四:es集群将为创建P0的副本分片,和重新创建和node1一起挂掉的R1副本分片,并让它们保存在和自己主分片不一样的节点上,也就是P0副本分片保存在node2上,R1副本分片保存在node3上,下次就算是在宕机,其中的一个节点存在有其他备份分片,保证了数据的不丢失,这就是es的高可用性
数据被存储到哪个分片是由公式决定的:
shard = hash(routing) % number_of_primary_shards
routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值
分片的实时性和查询性能
refresh
Elasticsearch 是基于 Apache Lucene 构建的,Lucene 是一个高性能的全文搜索引擎库。在Lucene中,索引是由多个段(segments)组成的,每个段是一个独立的倒排索引。
- 段(Segment):一个段是一个不可变的倒排索引,包含了一部分文档的数据。段是Lucene索引的基本单位。
- 内存缓冲区(Memory Buffer):新索引的文档首先被写入内存缓冲区,实际是就是写入到jvm中。
- 索引写入器(Index Writer):负责将内存缓冲区中的文档写入新的段。
refresh 操作的主要目的是将内存缓冲区中的文档刷新到一个新的段中,使得这些文档对搜索请求可见。以下是refresh操作的具体流程
- 内存缓冲区:
新索引的文档首先被写入内存缓冲区(jvm),这个内存缓冲区是位于jvm中 - 段创建:
当 refresh 操作被触发时,内存缓冲区中的文档被写入一个新的段,此操作在jvm中进行,所以段的创建也是在jvm中。
这个新的段是一个不可变的倒排索引,包含了内存缓冲区中的文档。 - 段注册:
新创建的段被注册到索引的搜索上下文中。
注册后,这些文档对搜索请求可见。 - 内存缓冲区清空:
内存缓冲区被清空,准备接收新的文档。
当我们向ES发送请求的时候,es貌似可以在我们发请求的同时进行搜索。而这个实时建索引被搜索的过程实际上是一次es 索引提交(commit)的过程,如果这个提交的过程直接将数据写入磁盘(fsync)必然会影响性能,所以es中设计了一种机制,即:默认情况下每隔一秒新写入的文档在jvm中解析写到文件缓存系统之中形成段,这样避免了比较损耗性能io操作,此时Lucene才能搜索得到,数据才可以提供搜索。
es默认每秒中执行一次refresh
默认情况下每个分片会每秒自动刷新一次。这就是为什么我们说 Elasticsearch 是 近 实时搜索: 文档的变化并不是立即对搜索可见,但会在一秒之内变为可见可搜索。
flush
如果数据在文件系统缓存之中是很有可能在意外的故障中丢失。这个时候就需要一种机制,可以将对es的操作记录下来,来确保当出现故障的时候,保留在文件系统缓存的数据不会丢失,并在重启的时候可以从这个记录中将数据恢复过来。elasticsearch提供了translog来记录这些操作。就是和数据库wal思想很像(日志先行)
flush包括:将事务日志(translog)中的操作持久化到磁盘(fsync)和清空内存中的缓冲区和事务日志两个流程。
一个文档被索引之后,就会被添加到内存缓冲区,并且追加到了translog
默认每秒refresh一次,refresh会清空内存缓存,但是不会清空translog
refresh操作不断发生,更多的文档被添加到内存缓冲区和追加到translog
translog周期性通过fsync进行刷盘,默认5s,可通过参数index.translog.sync_interval、index.translog.durability控制;并且每30分钟或当事务日志达到一定大小(512MB)时自动执行一次 flush 操作。保证应用重启后先确认最后记录的commit point,commit point之后的变更操作通过落盘的translog进行重构恢复段
虽然translog主要保存在内存中,但通过定期同步到磁盘,确保了数据的持久性,如果节点在两次同步之间宕机,内存中的translog数据可能会丢失。但是,由于同步频率较高(每5秒一次),这种数据丢失的风险非常小,如果es的数据非常重要,可以通过index.translog.sync_interval配置缩短日志自动的时间间隔,增加日志刷盘的频率,但会影响性能。如果es的数据不是那么重要,更es的性能,可减少日志刷盘的频率
fsync
fsync:每个新段在文件系统缓存被持久化到磁盘的过程叫做fsync
有时候我们创建了一个文档然后尝试搜索它,但却没有搜到。这个问题的解决办法是用 refresh API 执行一次手动刷新:
POST /_refresh //刷新所有的索引
POST /index_name/_refresh //只刷新(Refresh) 特定索引
es写性能优化
降低刷新频率(会影响实时性)
- 提高写性能,我们可以启动多线程去进行写操作
- 除了启动多线程外还可以通过调整刷新频率来提升性能。
虽然刷新(refresh)是比提交轻量很多的操作,但它还是会有性能开销,并不是所有的情况都需要每秒刷新。如果你想优化读写速度而不是近实时搜索,可以通过设置refresh_interval, 降低每个索引的刷新频率:
PUT /my_logs
{
"settings": {
"refresh_interval": "30s" //每30秒刷新 my_logs 索引,默认是每秒刷新
}
}
refresh_interval 是可以进行动态更新的,就是说在生产环境中,当你正在建立一个大的新索引时,可以先关闭自动刷新,待开始使用该索引时,再把它们调回来:
PUT /my_logs/_settings
{ "refresh_interval": -1 } //关闭实时更新
PUT /my_logs/_settings
{ "refresh_interval": "1s" }//开启每秒更新
refresh_interval 需要一个 持续时间 值, 例如 1s (1 秒) 或 2m (2 分钟)。 一个绝对值 1 表示的是 1毫秒 (会使你的集群陷入瘫痪)。
- 修改内存缓存大小(indices.memory.index_buffer_size)。
jvm内存缓存的数据满了或者是超过固定时间就会进行刷新操作,调整内存缓存大小可以有效的降低刷新频率
在elasticsearch.yml中可以这样设置:
indices.memory.index_buffer_size: 20%(默认是堆内存的10%)
indices.memory.min_index_buffer_size: 96mb(默认是48mb)
已经索引好的文档会先存放在内存缓存中,等待被写到到段(segment)中。缓存满的时候会触发段刷盘(吃i/o和cpu的操作)。默认最小缓存大小为48m,不太够,最大为堆内存的10%。对于大量写入的场景也显得有点小。
- 修改事务日志(translog)的刷新频率(fsync)(index.translog.sync_interval)和大小(index.translog.flush_threshold_size)
日志中记录了还在缓存中并未形成段的数据,会定时或者超过一定量后进行刷新
在elasticsearch.yml中可以这样设置:
index.translog.sync_interval:30s(默认5s)。
index.translog.flush_threshold_size:700mb(默认512mb)
index.translog.durability:async(默认request) //通过异步提交日志,不过可以会在宕机时丢失数据
- _id字段的使用,应尽可能避免自定义_id, 以避免针对ID的版本管理;建议使用ES的默认ID生成策略或使用数字类型ID做为主键。
es读性能优化
1.合理的分片的数量,分片数量越多每个分片的数据范围就越小,合理的分片数量能有效的提升每个分片的查询性能
2. 遵循通用的原则, 4~32G的内存给ES即可,其余留给lucene。内存对于 Elasticsearch 来说绝对是重要的,它可以被许多内存数据结构使用来提供更快的操作。但是另外一个内存消耗大户 非堆内存 (off-heap)的Lucene是用于索引的关键
关闭交换分区,防止内存发生交换导致性能下降 通过执行swapoff -a或者通过: 在elasticsearch.yml 中 bootstrap.memory_lock: true, 以保持JVM锁定内存,保证ES的性能。
es的锁机制
由于es需要处理一定的并发量的写操作,es是使用的乐观锁,主要是使用java的CAS技术实现乐观锁
正排索引
就是利用文档id查询文档内容,也就是通过key去查value
倒排索引
就是通过value去查key,关于倒排索引这里有一篇不错的文章可以供大家参考https://blog.csdn.net/qq_21312297/article/details/102496833
es索引的过程
es是先通过term Dictionary将每个单词出现在文档的位置地址这个过程是倒排索引,然后通过地址去查询文档,这个是正排索引,es就是倒排索引和正排索引的结合term Dictionary(单词字典):记录了所有文档的单词,es储存的文档内容越大词典也就越大,还记录了单词是放在哪些文档上的关联信息,通常term Dictionary会被拆分成单词的前缀组成一颗B+树,每个叶子节点的单词会记录着单词所在文档的位置地址
Posting List:记录了单词对应文档的集合,由Posting组成,而Posting中包含了文档id,单词出现频率,文档中单词的位置,单词在文档开始和结束的位置
文档字段类型
核心类型 字符串类型 string,text,keyword
整数类型 integer,long,short,byte
浮点类型 double,float,half_float,scaled_float
逻辑类型 boolean
日期类型 date
范围类型 range
二进制类型 binary
复合类型 数组类型 array
对象类型 object
嵌套类型 nested
地理类型 地理坐标类型 geo_point
地理地图 geo_shape
特殊类型 IP类型 ip
范围类型 completion
令牌计数类型 token_count
附件类型 attachment
抽取类型 percolator
基础语句
index操作
创建index:
put /index_name
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
查看index:
get /_cat/indices
删除index:
delete /index_name
document操作
创建id为2的文档:
DELETE /index_name/doc/2
创建id为1的文档:
put /index_name/_doc/1
{
"name":"es",
"user":"test"
}
创建自带id的文档:
post /index_name/_doc/
{
"name":"es",
"user":"test"
}
_routing:
如果在创建mapping时设置了_routing为true,那么创建文档的时候必须带上routing,否则出错:
post /index_name/_doc?routing=xxxxx
{
"name":"es",
"user":"test"
}
post /index_name/_doc/1?routing=xxxxx
{
"name":"es",
"user":"test"
}
批量创建文档:
put /u/_bulk
{"index":{"_id":3}}
{"username":"james","code":23,"age":32,"country":"usa","job":"basketball"}
{"index":{"_id":4}}
{"username":"haden","code":15,"age":30,"country":"usa","job":"basketball"}
{"index":{"_id":5}}
{"username":"bingbing","code":999,"age":35,"country":"chn","job":"act"}
{"index":{"_id":6}}
{"username":"susu","code":999,"age":25,"country":"chn","job":"great"}
{"index":{"_id":7}}
{"username":"hu","code":999,"age":36,"country":"chn","job":"act"}
批量修改文档:
POST _bulk
{"update":{"_index":"test","_type":"product","_id":1}}
{"doc":{ "price" : 5200, "productName" : "苹果11","memory":"128G" }}
{"update":{"_index":"test","_type":"product","_id": 2 }}
{"doc":{ "price" : 3100, "productName" : "小米10","memory":"268G" }}
{"update":{"_index":"test","_type":"product", "_id": 3 }}
{"doc":{ "price" : 1300, "productName" : "酷派大神","memory":"16G" }}
{"update":{"_index":"test","_type":"product", "_id": 4 }}
{"doc":{ "price" : 730, "productName" : "魅蓝note3","memory":"32G" }}
{"update":{"_index":"test","_type":"product", "_id": 6 }}
{"doc":{ "price" : 10000, "productName" : "macBook Pro","memory":"521G" }}
{"update":{"_index":"test","_type":"product", "_id": 7 }}
{"doc":{ "price" : 6500, "productName" : "华为P40","memory":"128G" }}
{"update":{"_index":"test","_type":"product","_id": 8 }}
{"doc":{ "price" : 3100, "productName" : "Ipad Air3","memory":"64G" }}
批量查询:
post /_mget
{
"docs": [
{
"_index": "test_index",
"_type": "doc",
"_id": "1"
},
{
"_index": "test_index",
"_type": "doc",
"_id": "Ai9cM3IBEyMN0HxifBpI"
}
]
修改文档:
POST /u/_doc/5/_update
{
"doc":{
"age":41,
"username":"kate"
}
}
根据查询的结果更改文档字段:
POST /u/_doc/_update_by_query
{
"script":{
"source":"ctx._source['brith']='1990-11-11 12:00:00'"
},
"query":{
"match_all":{
}
}
}
新增文档字段:
PUT /u/_mapping
{
"properties":{
"brith":{
"type":"date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
分词器
分词就是将文本转化为一系列单词的过程,也叫做文本分析,在es中称为Analysis,es默认的Analysis是standard
Analysis由三部分组成:
Character Filters:对原始文本进行处理,去掉一些特殊的符号标签,例如去除html的特殊标记符
Tokenizer:将原始文本按照一定的规则切分成单词
Toker Filters:对Tokenizer处理的单词进一步加工,例如大小写转换,删除没有意义的词等
分词器选择并测试:
post /_analyze
{
"analyzer":"simple",//选择simple分词器
"text":"elasticsearch是世界最好的搜索引擎"
}
es内置分词器:
Standard - 默认分词器,按词切分,小写处理
Simple - 按照非字母切分(符号被过滤), 小写处理
Stop - 小写处理,停用词过滤(the,a,is)
Whitespace - 按照空格切分,不转小写
Keyword - 不分词,直接将输入当作输出
Patter - 正则表达式,默认\W+(非字符分割)
Language - 提供了30多种常见语言的分词器
Customer 自定义分词器
中文分词器:
IK - 支持中英文单词分词,支持ik_smart,ik_maxword的模式,支持自定义词库、支持热更新单词词典
jieba - python最流行的分词器,支持繁体分词,自定义词典等
Mapping
Mapping类似数据库表数据类型定义,Mapping可以定义index下的字段名,字段类型。mapping创建后不支持修改和删除字段,只支持添加新字段
动态mapping
在mapping中有dynamic属性,默认是true,代表着动态mapping,在动态mapping下我们插入的文档,es会为我们自动识别文档中的字段类型,然后为我们创建相应的字段类型,下面是es自动识别后为我们创建的字段类型动态mapping可以降低用户的使用es的成本
静态mapping
当我们创建mapping时并指定dynamic属性为false,代表着静态mapping,在静态mapping下如果我们插入了mapping中没有定义的字段就会自动的进行忽略,忽略的意思就是插入没有定义的字段可以正常插入,但是搜索的时候会把字段给忽略了,查出来的结果不会包含mapping中不存在的字段并且这些字段也不支持索引
strict mapping
当我们把mapping设置为strict时,如果我们插入了mapping中没有定义的字段就会直接报错
index设置
mapping字段中的index字段代表着是否可以用来做搜索,默认是true
null_values
mapping字段中的null_values字段代表着字段遇到null时做怎么样的处理,es默认字段遇到null时会自动忽略
doc_values
字段的 doc_values 属性有两个值, true、false。默认为 true ,即开启。
当 doc_values 为 fasle 时,无法基于该字段排序、聚合、在脚本中访问字段值。
当 doc_values 为 true 时,ES 会增加一个相应的正排索引,这增加的磁盘占用,也会导致索引数据速度慢一些。
查看mapping
GET /index_name/_mapping
创建索引同时定义mapping
PUT /index_name
{
"settings": {
"number_of_shards": 4,
"number_of_replicas": 1
},
"mappings": {
"dynamic": "false",//定义mapping是否允许动态生成,默认是true,还有一个strict,strict下插入没有定义的字段会直接报错
"_routing": {
"required": true
},
"properties": {
"account": {
"type": "keyword",
"null_values":"暂无" //遇到null时显示为暂无,es默认遇到null会自动忽略
},
"firstname": {
"type": "keyword"
},
"nikename": {
"type": "keyword"
},
"create_time":{
"type":"date",
"format":"MM/dd/yyyy"
},
"obj_id": {
"type": "keyword",
"index": false, //是否支持索引,默认是true
"doc_values": false //false时不支持排序聚合
},
"bypass": {
"properties": {
"id": {
"type": "keyword"
},
"vpnmtype": {
"type": "keyword"
},
"vpnstype": {
"type": "keyword"
}
}
},
"caller_type": {
"type": "keyword"
},
"contend": {
"type": "text",
"fields": { //子字段类型,索引时可以使用contend.keyword
"keyword": {
"type": "keyword"
}
},
"analyzer": "ik_smart"
},
.......
Search API
URI Search
q:指定URL查询条件
df(default field):指定查询的字段,如果不写,es 会查询所有字段或者是q中指定的字段
sort:排序
timeout:指定超时时间,默认不超时
from,size:用于分页
GET /u/_search?q=basketball
GET /u/_search?q=job:basketball mvp//查询出所有job是basketball和mvp的文档
GET /u/_search?q=job:"basketball mvp"//查询出所有job是basketball mvp的文档
GET /u/_search?q=job:(basketball mvp)//查询job:basketball或者job:mvp
GET /u/_search?q=user.id:8a4f500d
GET /u/_search?q=basketball&df=job&sort=age:asc&from=1&size=3&timeout=1s//df指定q的值basketball从job字段查,分页从第一页开始,每页3条记录,查询时间不能超过1秒,超过一秒就退出查询
布尔操作:
要大写,不能小写
AND:&&
OR:||
NOT:!
GET /u/_search?q=basketball AND mvp //查询出所有包含basketball 并且同时有mvp 的字段
GET /u/_search?q=job:(basketball AND mvp)//查询出所有job是basketball并且有mvp的文档
GET /u/_search?q=job:(basketball NOT mvp)//查询出所有job是basketball并且没有mvp的文档
GET /u/_search?q=job:(basketball || mvp)//查询出所有job是basketball或者有mvp的文档
{
"profile":true"//查看es查询的过程
}
加减操作
+:在url写成%2B必须有 must
-:必须没有 must_not
GET /u/_search?q=basketball %2Bmvp//查询出所有是basketball 并且一定包含mvp的文档
GET /u/_search?q=(basketball %2Bmvp)//查询出所有是basketball 并且一定包含mvp的文档
GET /u/_search?q=basketball -2Bmvp//查询出所有是basketball 并且一定不包含mvp的文档
GET /u/_search?q=job:(basketball -2Bmvp)//查询出所有job是basketball 并且一定不包含mvp的文档
范围查询
闭区间 [ ] 开区间{ }
age:[1 TO 10]相当于1<=age<=10
age:[1 TO 10}相当于1<=age<10
age:[1 TO ]相当于age>=1
age:[* TO 10]相当于age<=10
GET /u/_search?q=basketball AND mvp age:>31 //查询出所有包含basketball 并且同时有mvp 的字段或者age大于31
GET /u/_search?q=basketball AND mvp AND age:>31//查询出所有包含basketball 并且同时有mvp 的字段并且age大于31
通配符查询
username:j?mes:匹配任意一个字符
username:jam*:匹配任意多个字符
username:j*s:匹配任意多个字符
GET /u/_search?q=j*s
GET /u/_search?q=username:j*s
GET /u/_search?q=j?mes
wildcard
wildcard类似上的模糊查询,相应于GET /u/_search?q=j*s的 DSL写法
post /u/_search
{
"query": {
"wildcard": {
"username.keyword":"j*s"
}
}
}
正则表达式查询
这个需要对正则表达的掌握能力,举两个例子
GET /u/_search?q=username:/j.{1,}/
GET /u/_search?q=username:/[张李].*亭{1,}.*/
query DSL 的写法
post /u/_search
{
"query": {
"regexp": {
"username":"j.*"
}
}
}
模糊匹配查询
GET /u/_search?q=job:basketball~1 //查询job中与basketball相差1个char值的文档,例如basketball 、basketball mvp
近似度查询
GET /u/_search?q=job:“basketball mvp“~1 //匹配到basketball mvp 前后中间可以允许插入1个词 basketball xxxx mvp\basketball mvp xxxx\xxxx basketball mvp都会匹配到,
Request Body Search
query DSL
字段查询
1、全文匹配
对text类型的字段查询时,会先分词
-
match:
分词后词语没有先后顺序,随意组合,这个适合非常适用于关键词全文检索
select * from u where job like '%basketball%' or job like '%mvp%'
post /u/_search
{
"profile":true,//查看es查询的过程
"query":{
"match":{
"job":"basketball mvp"//job字段查询即包含basketball 或者包含mvp的文档
}
}
}
select * from u where job like '%basketball%' and job like '%mvp%'
post /u/_search
{
"query": {
"match":{
"job":{
"query":"basketball mvp",//job必须同时包含basketball和mvp
"operator":"and"//默认是or
}
}
}
}
post /u/_search
{
"query": {
"match":{
"job":{
"query":"basketball mvp act",
"minimum_should_match":"2"//搜索出job字段中至少包含关键词其中两个才会匹配上
}
}
}
}
- match_all:
select * from u
post /u/_search
{
"query": {
"match_all": {}
}
}
-
match_phrase:
分词后词语存在先后顺序- match_phrase还是分词后去搜的
- 目标文档需要包含分词后的所有词
- 目标文档还要保持这些词的相对顺序和文档中的一致
- match_phrase的缺点就是性能比较差
以"hello world"为例,要求结果中必须包含hello和world,而且还要求他们是连着的,顺序也是固定的,hello that word不满足,world hello也不满足条件。
post /u/_search
{
"query": {
"match_phrase":{
"job":"basketball mvp"//查询包含basketball mvp的文档,
//与term不同的是term是从索引的单词找,match_phrase从文档的单词找
}
}
}
post /u/_search
{
"query": {
"match_phrase":{
"job":{
"query":"basketball act",
"slop":"1"//允许文档出现和搜索条件相差一个距离的文档
// 例如这里会匹配到 basketball xxxx act/basketball act xxxx/xxxx basketball act
}
}
}
}
-
query_string:
这个关键词和match类似,只是query_string更加暴力是所有字段中搜索范围更广泛。match则是需要指定字段的搜索的。但是query_string也可以指定字段并且可以使用and or等关键词,不指定字段默认全字段搜索,性能会很差
post /u/_search
{
"query": {
"query_string":{
"query":"basketball AND act"//匹配所有的字段中即有basketball又有act的文档
}
}
}
post /u/_search
{
"query": {
"query_string":{
"default_field":"job",//匹配job字段中即有basketball又有act的文档
"query":"basketball AND act"
}
}
}
post /u/_search
{
"query": {
"query_string":{
"fields":["job","username"],//只匹配job和username字段,并且每个字段都必须包含james 和 act
"query":"james and act"
}
}
}
2、精确查询
- term、terms:将查询语句作为一个单词查询,不做分词处理,查询时字段必须完全匹配包括长度顺序内容。使用这类关键词时字段的类型不要使用text,应当使用keyword类型,如果字段是text类型的话,我们查询时需要这样写: xxxx.keyword
select * from u where job="basketball mvp"
post /u/_search
{
"query": {
"term":{
"job.keyword":"basketball mvp"//这里在倒排索引中匹配basketball mvp
//最终的结果是匹配不到任何文档,因为索引中都是一个个被切分了的单词不存在“basketball mvp”
}
}
}
select * from u where job in ('basketball', 'mvp')
post /u/_search
{
"query": {
"terms":{
"job.keyword":["basketball","mvp"]
}
}
}
3、范围、排序、分页
- range: 范围查询 lt:小于、lte:小于等于、gt:大于、gte大于等于
select * from u where age between 30 and 40;
post /u/_search
{
"query" : {
"range" : {
"age" : {
"gte" : 30,
"lte" : 40
}
}
}
}
- sort,from,size:
POST /test/product/_search
{
"query":{
"match": {
"productName":"苹果小米华为"
}
},
"sort":{"price":"desc"},
"from":2,
"size":3
}
scroll:深分页,对于上面介绍的浅分页,如果请求的页数较少(假设每页20个docs), Elasticsearch不会有什么问题,但是如果页数较大时,比如请求第1000页,Elasticsearch不得不取出第1页到第1000页的所有docs,再去除第1页到第999页的docs,得到第1000页的docs。解决的方式就是使用scroll,scroll就是维护了当前索引段的一份快照信息--缓存
初始化的时候就像是普通的search一样 其中的scroll=3m代表当前查询的数据缓存3分钟
POST test/_search?scroll=3m
{
"query":{
"match_all": {}
}
}
在遍历时候,拿到上一次遍历中的_scroll_id,然后带scroll参数,3m代表重置过期时间
POST /_search/scroll
{
"scroll" : "3m",
"scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFjlxTWE1RTZwUmdpd3FZSkpsYm5kYXcAAAAAAAA-vxZBRWtUWDAyalM5YTZvbmx0YTJxQjZR"
}
相关性得分(_score)
依据:
- 词频:单词在该文档出现的频率越高,相关性越高
- 文档长度:文档越短,相关性越高
- 包含单词文档数量:文档包含某个单词的数量越少,相关性越高
复合查询
constant_score:
默认将其查询结果的文档得分都设置为1(如果不指定boost的话)或者指定boost的值,这个是当我们不关心检索词频率TF(Term Frequency)对搜索结果排序的影响时使用(简单了说就是不在乎得分)
POST /test/product/_search
{
"query":{
"constant_score": {
"filter": {
"match":{
"productName":"华为"
}
},
"boost": 1
}
}
}
GET test_index/_search
{
"query": {
"constant_score": {
"boost": 1.0,
"filter": {
"bool": {
"must": [
{
"terms": {
"qq": [
"7823709637249",
"7828688362980"
]
}
}
]
}
}
}
}
}
bool:
-
filter:使用filter查询时会有缓存,所以其效率很高,如果不使用相关性得分的排排序,推荐使用filter
post /u/_search
{
"query": {
"bool": {
"filter": {
"term": {
"job.keyword": "basketball"
}
}
}
}
}
post /u/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"job": "basketball"
}
},
{
"match": {
"job": "act"
}
}
]
}
}
}
- must:
post /u/_search
//查询文档中的job字段必须同时包含basketball和act
{
"query": {
"bool": {
"must": [
{
"term": {
"job": "basketball"
}
},
{
"match": {
"job": "act"
}
}
]
}
}
}
- must_not:
post /u/_search
//查询文档中的job字段必须同时包含basketball和act,而要求必须不能有mvp
{
"query": {
"bool": {
"must": [
{
"term": {
"job": "basketball"
}
},
{
"match": {
"job": "act"
}
}
],
"must_not": [
{
"match": {
"job": "mvp"
}
}
]
}
}
}
- should:默认至少满足一个条件,可以通过minimum_should_match控制满足个数
post /u/_search
//查询满足其中之一的查询即可,也就是job中包含basketball或者act或者mvp其中一个就能查询出来
{
"query": {
"bool": {
"should": [
{
"term": {
"job": "basketball"
}
},
{
"match": {
"job": "act"
}
},
{
"match_phrase": {
"job": "basketball mvp"
}
}
]
}
}
}
post /u/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"job": "basketball"
}
},
{
"match": {
"job": "act"
}
},
{
"match_phrase": {
"job": "basketball mvp"
}
}
],
"minimum_should_match":3//设置了满足条件为3
//查询结果同时满足其中三个也就job中包含basketball并且包含act和mvp就能查询出来都
}
}
}
- should与must\must_not:查询时必须满足或者必须不满足must/must_not,可以不满足should,如果可以满足should也一并查出
post /u/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"job": "act"
}
}
],
"must": [
{
"match":{
"job":"mvp"
}
}
]
}
}
}
should+must+filter
{
"query": {
"bool": {
"filter": [
{
"bool": {
"must": [
{
"terms": {
"boost": 1.0,
"msisdn": [
"7823709637249",
"7828603061831",
"7827817096436"
]
}
},
{
"range": {
"ts_m": {
"from": 1583622000017,
"to": 1618902000017
}
}
},
{
"bool": {
"should": [
{
"terms": {
"app_id": [
"0"
],
"boost": 1.0
}
},
{
"terms": {
"host": [
"res.wx.qq.com",
"bluedot.is.autonavi.com.gds.alibabadns.com"
]
}
},
{
"match": {
"col10": {
"query": "品种猫 部门 蛾子 刘微球"
}
}
}
]
}
}
]
}
}
]
}
}
}
- source:只希望返回个别字段即可
//只希望查询时返回username字段
GET /u/_search?_source=username
Count API
获取符合查询条件的文档数量
查询语句和上面的一样只是把URL修改了
post /u/_count
{
"query": {
"bool": {
"should": [
{
"match": {
"job": "act"
}
}
],
"must": [
{
"match":{
"job":"mvp"
}
}
]
}
}
}
排序、分页滚动
sort
-
数字类型排序遍历
单字段排序
post /u/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"job": "act"
}
}
],
"must": [
{
"match": {
"job": "mvp"
}
}
]
}
},
"sort": {
"age": "desc"//按照年龄倒叙排序
}
}
多字段排序
post /u/_search
{
"query": {
"match": {
"job": "basketball"
}
},
"sort": [
{
"age": "desc"
},
{
"_score": "desc"
}
]
}
-
字符串类型排序
按照字符串类型排序时不能排序text类型的,只能排序keyword的类型,所以在排序text类型时可以使用text.keyword的方式或者使用设置fielddata的属性,例如如下面的查询
post /u/_search
//text.keyword的方式
{
"query": {
"match": {
"job": "basketball"
}
},
"sort": {
"username.keyword":"asc"
}
}
设置fielddata为true
post /u/_mapping
{
"properties":{
"job":{
"type":"text",
"fielddata": true
}
}
}
然后直接进行text排序
POST /u/_search
{
"query": {
"match": {
"job": "basketball"
}
},
"sort": {
"job":"asc"
}
}
-
分页
from:开始位置 es默认不超过过
size:获取的总数
es默认不支持深分页换种说法就是es不支持大容量的分页,默认from+size不能超过10000
post /u/_search
{
"from":9996,
"size":4//from或者size再设置大一点就会报错
}
-
Scroll 分页滚动、遍历
利用Scroll可以改变分页查询中不能做深分页的缺点,Scroll查询方式的原理就是通过每次查询后,返回一个scroll_id。根据这个scroll_id 进行下一页的查询。可以把这个scroll_id理解为通常关系型数据库中的游标。scroll查询的数据是当前索引段的一份快照信息–缓存,所以scroll没有实时性
//创建一个scroll设置,并且设置存活时间是两分钟,也就是快照存活2分钟
GET /u/_search?scroll=2m
{
"query": {"match_all": {}},
"size": 1//设置每次滚动取1条数据,结果会返回
}
拿着scroll_id可以进行滚动
//滚动scroll
POST /_search?scroll
{
"scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAANAWb1IxVjQ0cFBUMy1jb1dFZUdRbDNKdw==",
"scroll":"5m"
}
-
Search After分页滚动、遍历
利用Search After可以解决Scroll不能实时滚动的缺点,但是Search After只能往下滚动,只支持向下滚动,不支持向上滚动,并且创建Search After必须要给文档排序
post /u/_search
{
"size":1,
"sort":{
"age":"desc"
}
}
将返回结果的sort中的值设置到滚动语句中,上图是56,设置search_after为56
GET /u/_search
{
"size":1,
"search_after":[56],
"sort":{
"age":"desc"
}
}
总结分页的使用场景
Aggregation 聚合查询
格式:多用于统计使用的,举个例子
例如统计从事各类职业的人数有多少
GET /u/_search
{
"size": 0,
"aggs": {
"tj": {
"terms": {
"field": "job.keyword"
}
}
}
}
返回结果
"aggregations": {
"tj": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 2,
"buckets": [
{
"key": "act",
"doc_count": 2
},
{
"key": "act av",
"doc_count": 2
},
{
"key": "basketball",
"doc_count": 2
},
........
]
}
}
聚合查询做的就是类似上面的事情,聚合查询分为4大类分别为:
- Bucket:分析统计类型,类似数据库的group by
- Metric:指标类型,统计如最大值,最小值,平均值等
- Pipeline:管道分析类型,基于上一级分析的结果进行再次分析
- Matrix:矩阵分析类型,是es的多维度分析
Bucket
bucket的作用类似数据库的分组,把不同的分析结果装到几个桶里
- terms:按照词语分桶,分桶字段如果是keyword不会进行分词,直接按照整个单词分桶,如果是text则会先分词在分桶,不过使用text进行聚合需要设置fielddata为true
GET /u/_search
{
"aggs": {
"aggs_bulk": {
"terms": {
"field": "job.keyword",
"size":3//指定返回的个数
}
}
}
}
返回结果
"aggregations": {
"aggs_bulk": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 11,
"buckets": [
{
"key": "act",
"doc_count": 2
},
{
"key": "act av",
"doc_count": 2
},
{
"key": "basketball",
"doc_count": 2
}
]
}
}
-
range
范围聚合,统计数据指定范围的数量
GET /u/_search
{
"aggs": {
"aggs_range": {
"range": {
"field": "age",
"ranges": [
{
"to": 36//统计年龄小于36的人数
},
{
"from": 35,
"to": 41
}//统计年龄35.0-41.0之间的人数
]
}
}
}
}
返回结果
"aggregations": {
"aggs_range": {
"buckets": [
{
"key": "*-36.0",
"to": 36.0,
"doc_count": 9
},
{
"key": "35.0-41.0",
"from": 35.0,
"to": 41.0,
"doc_count": 7
}
]
}
}
-
date range
指定日期范围的分桶
GET monitor_temp/_search
{
"aggs": {
"aggs_range": {
"date_range": {
"field": "ts_m",
"format":"yyyy-MM-dd HH:mm:ss",
"ranges": [
{
"from":"2020-04-01 00:00:00",
"to": "2020-04-30 12:00:00"
},
{
"from":"2020-05-01 00:00:00",
"to": "2020-05-30 12:00:00"
}
]
}
}
}
}
返回部分结果
"aggregations" : {
"aggs_range" : {
"buckets" : [
{
"key" : "2020-04-01 00:00:00-2020-04-30 12:00:00",
"from" : 1.5856992E12,
"from_as_string" : "2020-04-01 00:00:00",
"to" : 1.588248E12,
"to_as_string" : "2020-04-30 12:00:00",
"doc_count" : 3
},
{
"key" : "2020-05-01 00:00:00-2020-05-30 12:00:00",
"from" : 1.5882912E12,
"from_as_string" : "2020-05-01 00:00:00",
"to" : 1.59084E12,
"to_as_string" : "2020-05-30 12:00:00",
"doc_count" : 3
}
]
}
}
-
histogram
统计固定间隔的数据数量。类似一个树形柱状图
GET /u/_search
{
"aggs": {
"aggs_his": {
"histogram": {
"field": "age",
"interval":5,//统计年龄间隔5岁的各个阶段的人数
"extended_bounds": //查询范围
{
"min":30,
"max": 56
}
}
}
}
}
返回结果
"aggregations": {
"aggs_his": {
"buckets": [
{
"key": 25.0,
"doc_count": 2
},
{
"key": 30.0,
"doc_count": 6
},
{
"key": 35.0,
"doc_count": 7
},
{
"key": 40.0,
"doc_count": 1
},
{
"key": 45.0,
"doc_count": 0
},
{
"key": 50.0,
"doc_count": 0
},
{
"key": 55.0,
"doc_count": 1
}
]
}
}
从query的结果集中,以ts_m字段指定的范围,取每隔1小时取一次数据量统计
GET monitor_temp/_search
{
"query": {
"range": {
"ts_m": {
"gte": 1617273000000,
"lte": 1617359400000
}
}
},
"aggs": {
"aggs_his": {
"histogram": {
"field": "ts_m",
"format":"yyyy-MM-dd HH:mm",
"interval":3600000,
"extended_bounds":
{
"max":1617359400000,
"min":1617273000000
}
}
}
}
}
返回的部分结果
"aggregations" : {
"aggs_his" : {
"buckets" : [
{
"key_as_string" : "2021-04-01 10:00",
"key" : 1.6172712E12,
"doc_count" : 0
},
{
"key_as_string" : "2021-04-01 11:00",
"key" : 1.6172748E12,
"doc_count" : 0
},
{
"key_as_string" : "2021-04-01 12:00",
"key" : 1.6172784E12,
"doc_count" : 0
},
{
"key_as_string" : "2021-04-01 13:00",
"key" : 1.617282E12,
"doc_count" : 0
},
{
"key_as_string" : "2021-04-01 14:00",
"key" : 1.6172856E12,
"doc_count" : 0
},
{
"key_as_string" : "2021-04-01 15:00",
"key" : 1.6172892E12,
"doc_count" : 0
},
.......
]
}
}
-
date histogram
用于日期的histogram
GET /u/_search
{
"aggs": {
"aggs_his": {
"date_histogram": {
"field": "brith",
"interval":"year",//出生日期以每一年为间隔
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
输出结果
"aggregations": {
"aggs_his": {
"buckets": [
{
"key_as_string": "1954-01-01 00:00:00",
"key": -504921600000,
"doc_count": 1
},
{
"key_as_string": "1959-01-01 00:00:00",
"key": -347155200000,
"doc_count": 0
},
{
"key_as_string": "1967-01-01 00:00:00",
"key": -94694400000,
"doc_count": 0
},
{
"key_as_string": "1968-01-01 00:00:00",
"key": -63158400000,
"doc_count": 0
},
{
"key_as_string": "1969-01-01 00:00:00",
"key": -31536000000,
"doc_count": 0
},
.................
从query的结果集中,以ts_m字段指定的范围,取每隔1小时取一次数据量统计,返回的结果是上海时间
GET monitor_temp/_search
{
"aggregations": {
"aggs_his": {
"date_histogram": {
"extended_bounds": {
"max":1617359400000,
"min":1617273000000
},
"field": "ts_m",
"interval": "1h",
"offset": 0,
"format":"MM-dd HH:mm",
"order": {
"_key": "asc"
},
"time_zone": "Asia/Shanghai"
}
}
},
"query": {
"range": {
"ts_m": {
"from": 1617273000000,
"to": 1617359400000
}
}
}
}
部分返回结果
"aggregations" : {
"aggs_his" : {
"buckets" : [
{
"key_as_string" : "04-01 18:00",
"key" : 1617271200000,
"doc_count" : 0
},
{
"key_as_string" : "04-01 19:00",
"key" : 1617274800000,
"doc_count" : 0
},
{
"key_as_string" : "04-01 20:00",
"key" : 1617278400000,
"doc_count" : 0
},
{
"key_as_string" : "04-01 21:00",
"key" : 1617282000000,
"doc_count" : 0
},
{
"key_as_string" : "04-01 22:00",
"key" : 1617285600000,
"doc_count" : 0
},
Metric
- 单值分析
分析只输出一个结果
- min、max、avg、sum
//查询age的最小值
GET test/_search
{
"aggs": {
"max": {
"min": {
"field": "price"
}
}
}
}
输出部分结果
"aggregations" : {
"max" : {
"value" : 130.0
}
//查询age的最大值
GET /u/_search
{
"size": 0,
"aggs": {
"aggs_name": {
"max": {
"field": "age"
}
}
}
}
//查询age的总值
GET /u/_search
{
"size": 0,
"aggs": {
"aggs_name": {
"sum": {
"field": "age"
}
}
}
}
//查询age的平均值
GET /u/_search
{
"size": 0,
"aggs": {
"aggs_name": {
"avg": {
"field": "age"
}
}
}
}
可以将他们组合在一起
GET test/_search
{
"size": 0,
"aggs": {
"aggs_avg": {
"avg": {
"field": "price"
}
},
"aggs_sum": {
"sum": {
"field": "price"
}
},
"aggs_max": {
"max": {
"field": "price"
}
},
"aggs_min": {
"min": {
"field": "price"
}
}
}
}
输出部分结果
"aggregations" : {
"aggs_sum" : {
"value" : 134633.0
},
"aggs_min" : {
"value" : 130.0
},
"aggs_avg" : {
"value" : 5853.608695652174
},
"aggs_max" : {
"value" : 22788.0
}
}
- cardinality:统计某个字段有多少种类
GET test/_search
{
"size": 0,
"aggs": {
"aggs_cardinality": {
"cardinality": {
"field": "productName.keyword" //统计productName的不同种类
}
}
}
}
部分输出结果
"aggregations" : {
"aggs_cardinality" : {
"value" : 23
}
}
- 多值分析
分析可以输出多个结果
stats、extended_stats、percentiles:
- stats:返回一系列统计类型,包括min,max,avg,sum,count
GET /u/_search
{
"size": 0,
"aggs": {
"aggs_stats": {
"stats": {
"field": "age"
}
}
}
}
输出的结果
"aggregations": {
"aggs_count": {
"count": 17,
"min": 25.0,
"max": 56.0,
"avg": 34.64705882352941,
"sum": 589.0
}
}
- extended_stats:计算方差、标准差等使用extended_stats,是对stats功能的扩展
GET /u/_search
{
"size": 0,
"aggs": {
"aggs_estats": {
"extended_stats": {
"field": "age"
}
}
}
}
输出结果
"aggregations": {
"aggs_estats": {
"count": 17,
"min": 25.0,
"max": 56.0,
"avg": 34.64705882352941,
"sum": 589.0,
"sum_of_squares": 21175.0,
"variance": 45.16955017301028,
"std_deviation": 6.720829574763094,
"std_deviation_bounds": {
"upper": 48.0887179730556,
"lower": 21.205399674003225
}
}
}
- percentiles:统计数据的所占百分位比重,例如统计数据中百分之多少的人是在哪个年龄段
GET /u/_search
{
"size": 0,
"aggs": {
"aggs_percentile": {
"percentiles": {
"field": "age"
}
}
}
}
返回结果
"aggregations": {
"aggs_percentile": {
"values": {
"1.0": 24.999999999999996,//24岁年龄段的有百分之1
"5.0": 25.0,//25岁年龄段的有百分之5
"25.0": 31.0,//31岁年龄段的有百分之25
"50.0": 35.0,//35岁年龄段的有百分之50
"75.0": 36.0,//36岁年龄段的有百分之75
"95.0": 50.74999999999998,//51岁年龄段的有百分之95
"99.0": 56.0//56岁年龄段的有百分之99
}
}
}
我们可以指定某个百分数范围内对应的数值是哪些
GET /u/_search
{
"size": 0,
"aggs": {
"aggs_percentile": {
"percentiles": {
"field": "age",
"percents":[50,75]
}
}
}
}
返回结果
"aggregations": {
"aggs_percentile": {
"values": {
"50.0": 35.0,
"75.0": 36.0
}
}
}
- aggs_percentile:我们查某些数据范围中是处于哪个百分位
GET test/_search
{
"aggs": {
"p_r": {
"percentile_ranks": {
"field": "price",
"values": [
19988,
5800
]
}
}
}
}
输出结果
"aggregations" : {
"p_r" : {
"values" : {
"5800.0" : 63.47826086956522,
"19988.0" : 94.54434671825976
}
}
}
- top_hits:我们希望能从分组统计的基础上再获取统计信息
下面是按照职业进行分组,但是我们要取每个分组中前十名
GET /u/_search
{
"size": 0,
"aggs": {
"tj": {
"terms": {
"field": "job.keyword"
},
"aggs": {
"my_top": {
"top_hits": {
"size": 10,
"sort": [
{
"age": {
"order": "desc"
}
}
]
}
}
}
}
}
}
-
桶中桶 bucket+Metric的组合拳
在分组后,对每个组在进行统计
GET /u/_search
//统计在职业分组后,再统计每组职业的年龄段占比多少
{
"aggs": {
"aggs_term": {
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs": {
"aggs_percentile": {
"percentiles": {
"field": "age"
}
}
}
}
}
}
返回结果
"aggregations": {
"aggs_term": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 2,
"buckets": [
{
"key": "act",
"doc_count": 2,
"aggs_percentile": {
"values": {
"1.0": 36.0,
"5.0": 36.0,
"25.0": 36.0,
"50.0": 38.5,
"75.0": 41.0,
"95.0": 41.0,
"99.0": 41.0
}
}
},
{
"key": "act av",
"doc_count": 2,
"aggs_percentile": {
"values": {
"1.0": 25.0,
"5.0": 25.0,
"25.0": 25.0,
"50.0": 30.0,
"75.0": 35.0,
"95.0": 35.0,
"99.0": 35.0
}
}
},
{
"key": "basketball",
"doc_count": 2,
"aggs_percentile": {
"values": {
"1.0": 30.999999999999996,
"5.0": 31.0,
"25.0": 31.0,
"50.0": 31.5,
"75.0": 32.0,
"95.0": 32.0,
"99.0": 32.0
}
}
},
{
"key": "basketball mvp",
"doc_count": 2,
"aggs_percentile": {
"values": {
"1.0": 29.999999999999996,
"5.0": 30.0,
"25.0": 30.0,
"50.0": 31.0,
"75.0": 32.0,
"95.0": 32.0,
"99.0": 32.0
}
}
},
{
"key": "basketball mvp act",
"doc_count": 2,
"aggs_percentile": {
"values": {
"1.0": 30.999999999999996,
"5.0": 31.0,
"25.0": 31.0,
"50.0": 43.5,
"75.0": 56.0,
"95.0": 56.0,
"99.0": 56.0
}
}
},
{
"key": "basketball act",
"doc_count": 1,
"aggs_percentile": {
"values": {
"1.0": 33.0,
"5.0": 33.0,
"25.0": 33.0,
"50.0": 33.0,
"75.0": 33.0,
"95.0": 33.0,
"99.0": 33.0
}
}
},
{
"key": "cartoonman hai",
"doc_count": 1,
"aggs_percentile": {
"values": {
"1.0": 36.0,
"5.0": 36.0,
"25.0": 36.0,
"50.0": 36.0,
"75.0": 36.0,
"95.0": 36.0,
"99.0": 36.0
}
}
},
.............
]
}
-
Pipeline管道聚合分析
针对上层分析的结果进行再次分析,其分析的结果会输出到上层的结果中,根据输出的位置不同分为两类:
-
内嵌到上层的结果中
min、max、avg、stats、percentiles
min_bucket\max_bucket\avg_bucket\stats_bucket\percentiles_bucket
用法差不多,举一个例子,
//计算每一类职业的平均收入,然后找出平均收入最低的职业
GET /u/_search
{
"aggs": {
"aggs_term": {
"terms": {
"field": "job.keyword"
},
"aggs": {
"aggs_avg": {
"avg": {
"field": "income"
}
}
}
},
"min_pipeline": {//给pipeline起名字,pipeline与aggs的子项aggs_term同级,导致后面的buckets_path要写成aggs_term>aggs_avg
//分析谁就与谁同级
"min_bucket": {//min_bucket是关键词,如果是算最大值换成max_bucket,平均值换成avg_bucket、同时获取多个指标则用stats_bucket
"buckets_path": "aggs_term>aggs_avg"//aggs的所属结构
}
}
}
}
输出结果
"min_pipeline": {
"value": 1000.0,//平均收入最低是1000
"keys": [//这几个职业都是收入最低的
"cartoonman hai",
"cartoonman hot",
"cartoonman qun",
"cartoonman seven"
]
}
结果与上层的结果同级
-
聚合分析的范围
aggs与query关键字同级,在query执行后,aggs针对query执行的结果进行分析,也就是说query查出来结果就是aggs的分析范围
也可以在aggs内部使用filter关键词限定范围
GET /u/_search
{
"aggs": {
"aggs_filter": {
"filter": {//利用filter限定aggs范围
"match": {
"job":"basketball"
}
},
"aggs":{
"job_aggs":{
"terms":{
"field":"job.keyword"
}
}
}
}
}
}
还要一种限定范围的方法是post_filter在aggs分析之后在进行筛选
{
"aggs": {
"job_aggs": {
"terms": {
"field": "job.keyword"
}
}
},
"post_filter":{
"match":{
"job.keyword":"mvp"
}
}
}
聚合排序sort
GET /u/_search
{
"aggs": {
"aggs_term": {
"terms": {
"field": "job.keyword",
"order":{
"aggs_avg":"desc"//以子聚合分析的结果排序
}
},
"aggs": {
"aggs_avg": {
"avg": {
"field": "income"
}
}
}
}
}
}
返回结果
"buckets": [
{
"key": "soccerball mvp",
"doc_count": 1,
"aggs_avg": {
"value": 5.0E8
}
},
{
"key": "basketball mvp",
"doc_count": 2,
"aggs_avg": {
"value": 1.3E8
}
},
{
"key": "basketball",
"doc_count": 2,
"aggs_avg": {
"value": 8.5E7
}
},
{
"key": "act",
"doc_count": 2,
"aggs_avg": {
"value": 1.01E7
}
},
{
"key": "basketball mvp act",
"doc_count": 2,
"aggs_avg": {
"value": 750000.0
}
},
{
"key": "basketball act",
"doc_count": 1,
"aggs_avg": {
"value": 600000.0
}
},
{
"key": "act av",
"doc_count": 2,
"aggs_avg": {
"value": 40000.0
}
},
{
"key": "great",
"doc_count": 1,
"aggs_avg": {
"value": 8000.0
}
},
.........................
以外层的key或者聚合分析数排序
GET /u/_search
{
"aggs": {
"aggs_term": {
"terms": {
"field": "job.keyword",
"order": [
{
"_key": "desc"//聚合结果的key排序
},
{
"_count": "desc"//聚合数量排序
}
]
},
"aggs": {
"aggs_avg": {
"avg": {
"field": "income"
}
}
}
}
}
}
返回结果
"buckets": [
{
"key": "soccerball mvp",
"doc_count": 1,
"aggs_avg": {
"value": 5.0E8
}
},
{
"key": "great",
"doc_count": 1,
"aggs_avg": {
"value": 8000.0
}
},
{
"key": "cartoonman seven",
"doc_count": 1,
"aggs_avg": {
"value": 1000.0
}
},
{
"key": "cartoonman qun",
"doc_count": 1,
"aggs_avg": {
"value": 1000.0
}
},
{
"key": "cartoonman hot",
"doc_count": 1,
"aggs_avg": {
"value": 1000.0
}
},
{
"key": "cartoonman hai",
"doc_count": 1,
"aggs_avg": {
"value": 1000.0
}
},
..........................