从零学Elasticsearch系列——深入搜索(Query、Filter、Aggregation)
系列文章:
- 从零学Elasticsearch系列——基础概念
- 从零学Elasticsearch系列——环境搭建
- 从零学Elasticsearch系列——使用kibana实现ES基本的操作
- 从零学Elasticsearch系列——深入搜索(Query、Filter、Aggregation)
- 从零学Elasticsearch系列——JAVA API操作
- 从零学Elasticsearch系列——集成中文分词器IK
- 从零学Elasticsearch系列——构建ES集群
- 从零学Elasticsearch系列——搭建ELK Nginx日志分析平台
搜索方式
搜索有两种方式:一种是通过URL
参数进行搜索,另一种是通过DSL(Request Body)
进行搜索
DSL:Domain Specified Language,特定领域语言
使用请求体可以让你的JSON数据以一种更加可读和更加富有展现力的方式发送。
导入测试数据集
# 批量插入测试数据
POST /zpark/user/_bulk
{"index":{"_id":1}}
{"name":"zs","realname":"张三","age":18,"birthday":"2018-12-27","salary":1000.0,"address":"北京市昌平区沙阳路55号"}
{"index":{"_id":2}}
{"name":"ls","realname":"李四","age":20,"birthday":"2017-10-20","salary":5000.0,"address":"北京市朝阳区三里屯街道21号"}
{"index":{"_id":3}}
{"name":"ww","realname":"王五","age":25,"birthday":"2016-03-15","salary":4300.0,"address":"北京市海淀区中关村大街新中关商城2楼511室"}
{"index":{"_id":4}}
{"name":"zl","realname":"赵六","age":20,"birthday":"2003-04-19","salary":12300.0,"address":"北京市海淀区中关村软件园9号楼211室"}
{"index":{"_id":5}}
{"name":"tq","realname":"田七","age":35,"birthday":"2001-08-11","salary":1403.0,"address":"北京市海淀区西二旗地铁辉煌国际大厦负一楼"}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
查询(Query)
1. 查看所有并按照年龄降序排列
查询所有并排序
- URL实现
GET /zpark/user/_search?q=*&sort=age:desc&pretty
- 1
- DSL实现
GET /zpark/user/_search { "query":{ "match_all":{} # 查询所有 }, "sort":{ "age":"desc" # 按年龄倒序排列 } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
2. 查询第2页的用户(每页显示2条)
分页查询
- URL实现
GET /zpark/user/_search?q=*&sort=_id:asc&from=2&size=2
- 1
- DSL实现
GET /zpark/user/_search { "query":{ "match_all":{} # 查询所有 }, "sort":{ "_id":"asc" # 按年龄倒序排列 }, "from":2, # 从(nowPage-1)*pageSize检索 "size":2 # 查 pageSize条 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
3. 查询address
在海淀区的所有用户,并高亮
基于全文检索的查询(分析检索关键词 匹配索引库 返回结果)
- DSL实现
GET /zpark/user/_search { "query": { "match": { # 注意: match查询会分词 如:海淀区 会分词为 海 | 淀 | 区 "address":"海淀区" } }, "highlight": { "fields": { # 需要高亮的字段列表 "address": {} } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
4. 查询name
是zs
关键字的用户
基于Term词元查询
- URL实现
GET /zpark/user/_search?q=name:zs
- 1
- DSL实现
GET /zpark/user/_search { "query":{ "term": { "name": { "value": "zs" } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5. 查询年龄在20~30
岁之间的用户
基于范围查询
- DSL实现
GET /zpark/user/_search { "query": { "range": { "age": { "gte": 20, # 大于等于 大于用 gt "lte": 30 # 小于等于 小于用 lt } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
6. 查询真实姓名以张
开头的用户
基于前缀(prefix)查询
- DSL实现
GET /zpark/user/_search { "query": { "prefix": { "realname": { "value": "李" } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
7. 查询名字已s
结尾的用户
基于通配符(wildcard)的查询
?
匹配一个字符*
匹配0~n个字符
- DSL实现
GET /zpark/user/_search { "query": { "wildcard": { "name": { "value": "*s" } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
8. 查询id
为1,2,3的用户
基于Ids的查询
- DSL实现
GET /zpark/user/_search { "query": { "ids": { "values": [1,2,3] } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
9. 模糊查询realname
中包含张
关键字的用户
基于Fuzzy的查询
- DSL实现
GET /zpark/user/_search { "query": { "fuzzy": { "realname": {"value": "张"} } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
10. 查询age
在15-30岁之间并且name
必须通配z*
基于Boolean的查询(多条件查询)
must
:查询结果必须符合该查询条件(列表)。should
:类似于or的查询条件。must_not
:查询结果必须不符合查询条件(列表)。
- DSL实现
GET /zpark/user/_search { "query": { "bool": { "must": [ #年龄在15~30岁之间并且必须名字通配z* { "range": { "age": { "gte": 15, "lte": 30 } } }, { "wildcard": { "name": { "value": "z*" } } } ], "must_not": [ # 正则查询 name必须不能以s结尾 { "regexp": { "name": ".*s" } } ] } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
过滤器(Filter)
其实准确来说,ES中的查询操作分为2种:查询(query)和过滤(filter)。查询即是之前提到的query查询,它(查询)默认会计算每个返回文档的得分,然后根据得分排序。而过滤(filter)只会筛选出符合的文档,并不计算得分,且它可以缓存文档。所以,单从性能考虑,过滤比查询更快。
换句话说,过滤适合在大范围筛选数据,而查询则适合精确匹配数据。一般应用时,应先使用过滤操作过滤数据,然后使用查询匹配数据。
过滤器使用
GET /zpark/user/_search
{
"query":{
"bool": {
"must": [
{"match_all": {}}
],
"filter": { # 过滤年龄大于等于25岁的用户
"range": {
"age": {
"gte": 25
}
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
注意: 过滤查询运行时先执行过滤语句,后执行普通查询
过滤器的类型
1. term
、terms
Filter
term、terms的含义与查询时一致。term用于精确匹配、terms用于多词条匹配
GET /zpark/user/_search
{
"query":{
"bool": {
"must": [
{"match_all": {}}
],
"filter": {
"terms": {
"name": [
"zs",
"ls"
]
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
2. ranage
filter
3. exists
filter
exists
过滤指定字段没有值的文档
GET /zpark/user/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {}
}
],
"filter": { # 排除salary为null的结果
"exists": {
"field": "salary"
}
}
}
},
"sort": [
{
"_id": {
"order": "asc"
}
}
]
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
或(相反操作)
GET /zpark/user/_search
{
"query": {
"bool": {
"must_not": [
{
"exists": {
"field": "salary"
}
}
]
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
4. ids
filter
需要过滤出若干指定_id的文档,可使用标识符过滤器(ids)
GET /zpark/user/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "昌平区"
}
}
],
"filter": {
"ids": { # id 过滤器
"values": [
1,
2,
3
]
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
5. 其余使用方式可查阅官网
Note:
Query和Filter更详细的对比可参考:https://blog.csdn.net/laoyang360/article/details/80468757
聚合(Aggregations)
https://www.elastic.co/guide/en/elasticsearch/reference/6.x/search-aggregations.html
聚合提供了功能可以分组并统计你的数据。理解聚合最简单的方式就是可以把它粗略的看做SQL的GROUP BY操作和SQL的聚合函数。
ES中常用的聚合:
- metric(度量)聚合:度量类型聚合主要针对的number类型的数据,需要ES做比较多的计算工作,类似于关系型数据库的组函数操作
- bucketing(桶)聚合:划分不同的“桶”,将数据分配到不同的“桶”里。非常类似sql中的group语句的含义,类似于关系型数据库的分组操作
ES中的聚合API如下:
"aggregations" : { // 表示聚合操作,可以使用aggs替代
"<aggregation_name>" : { // 聚合名,可以是任意的字符串。用做响应的key,便于快速取得正确的响应数据。
"<aggregation_type>" : { // 聚合类别,就是各种类型的聚合,如min等
<aggregation_body> // 聚合体,不同的聚合有不同的body
}
[,"aggregations" : { [<sub_aggregation>]+ } ]? // 嵌套的子聚合,可以有0或多个
}
[,"<aggregation_name_2>" : { ... } ]* // 另外的聚合,可以有0或多个
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
度量(metric)聚合
1. Avg Aggregation
平均值查询,作用于number类型字段上。如:查询用户的平均年龄
POST /zpark/user/_search
{
"aggs": {
"age_avg": {
"avg": {"field": "age"}
}
}
}
-------------------------------------------------------------------
{
......
"aggregations": {
"age_avg": {
"value": 23.6
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
也可以先过滤,再进行统计,如:
POST /zpark/user/_search
{ "query": {
"ids": {
"values":[1,2,3]
}
},
"aggs": {
"age_avg": {
"avg": {"field": "age"}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
2. Max Aggregation
最大值查询。如:查询员工的最高工资
POST /zpark/user/_search
{
"aggs": {
"max_salary": {
"max": {
"field": "salary"
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
3. Min Aggregation
4. Sum Aggregation
5. [Stats Aggregation](Stats Aggregation)
统计查询,一次性统计出某个字段上的常用统计值
POST /zpark/user/_search
{
"aggs": {
"max_salary": {
"stats": {
"field": "salary"
}
}
}
}
--------------------------------------------
{
....
"aggregations": {
"max_salary": {
"count": 4,
"min": 1000,
"max": 12300,
"avg": 5650,
"sum": 22600
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
桶(bucketing)聚合
1. Range Aggregation
自定义区间范围的聚合,我们可以自己手动地划分区间,ES会根据划分出来的区间将数据分配不同的区间上去。
如: 统计0-20岁,20-35岁,35~60岁用户人数
POST /zpark/user/_search
{
"aggs": {
"age_ranges": {
"range": {
"field": "age",
"ranges": [
{
"from": 0,
"to": 20
},
{
"from": 20,
"to": 35
},
{
"from": 35,
"to": 60
}
]
}
}
}
}
----------------------------------------------------------
{
......
"aggregations": {
"age_ranges": {
"buckets": [
{
"key": "0.0-20.0",
"from": 0,
"to": 20,
"doc_count": 1 # 区间范围的文档数量
},
{
"key": "20.0-35.0",
"from": 20,
"to": 35,
"doc_count": 3
},
{
"key": "35.0-60.0",
"from": 35,
"to": 60,
"doc_count": 1
}
]
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
2. Terms Aggregation
自定义分组依据Term,对分组后的数据进行统计
如:根据年龄分组,统计相同年龄的用户
POST /zpark/user/_search
{
"aggs": {
"age_counts":{
"terms": {
"field": "age",
"size": 2 // 保留2个统计结果
}
}
}
}
----------------------------------------------------------
{
......
"aggregations": {
"age_counts": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 2,
"buckets": [
{
"key": 20,
"doc_count": 2
},
{
"key": 18,
"doc_count": 1
}
]
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
3. Date Range Aggregation
时间区间聚合专门针对date类型的字段,它与Range Aggregation的主要区别是其可以使用时间运算表达式。
- now+10y:表示从现在开始的第10年。
- now+10M:表示从现在开始的第10个月。
- 1990-01-10||+20y:表示从1990-01-01开始后的第20年,即2010-01-01。
- now/y:表示在年位上做舍入运算。
如: 统计生日在2018年、2017年、2016年的用户
POST /zpark/user/_search
{
"aggs": {
"date_counts": {
"date_range": {
"field": "birthday",
"format": "yyyy-MM-dd",
"ranges": [
{
"from": "now/y", # 当前年的1月1日
"to": "now" # 当前时间
},
{
"from": "now/y-1y", # 当前年上一年的1月1日
"to":"now/y" # 当前年的1月1日
},
{
"from": "now/y-2y",
"to":"now/y-1y"
}
]
}
}
}
}
-------------------------------------------------------------------------------
{
.....
"aggregations": {
"date_counts": {
"buckets": [
{
"key": "2016-01-01-2017-01-01",
"from": 1451606400000,
"from_as_string": "2016-01-01",
"to": 1483228800000,
"to_as_string": "2017-01-01",
"doc_count": 1
},
{
"key": "2017-01-01-2018-01-01",
"from": 1483228800000,
"from_as_string": "2017-01-01",
"to": 1514764800000,
"to_as_string": "2018-01-01",
"doc_count": 1
},
{
"key": "2018-01-01-2018-12-26",
"from": 1514764800000,
"from_as_string": "2018-01-01",
"to": 1545847233691,
"to_as_string": "2018-12-26",
"doc_count": 0
}
]
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
4. Histogram Aggregation
直方图聚合,它将某个number类型字段等分成n份,统计落在每一个区间内的记录数。它与前面介绍的Range聚合非常像,只不过Range可以任意划分区间,而Histogram做等间距划分。既然是等间距划分,那么参数里面必然有距离参数,就是interval参数。
如:根据年龄间隔(5岁)统计
POST /zpark/user/_search
{
"aggs": {
"histogram_age": {
"histogram": {
"field": "age",
"interval": 5
}
}
}
}
-------------------------------------------------------------------------------
{
......
"aggregations": {
"histogram_age": {
"buckets": [
{
"key": 15,
"doc_count": 1
},
{
"key": 20,
"doc_count": 2
},
{
"key": 25,
"doc_count": 1
},
{
"key": 30,
"doc_count": 0
},
{
"key": 35,
"doc_count": 1
}
]
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
5. Date Histogram Aggregation
日期直方图聚合,专门对时间类型的字段做直方图聚合。这种需求是比较常用见得的,我们在统计时,通常就会按照固定的时间断(1个月或1年等)来做统计。
如:按年统计用户
POST /zpark/user/_search
{
"aggs": {
"date_histogram": {
"date_histogram": {
"field": "birthday",
"interval": "year",
"format": "yyyy-MM-dd"
}
}
}
}
-------------------------------------------------------------------------------
{
......
"aggregations": {
"date_histogram": {
"buckets": [
{
"key_as_string": "2001-01-01",
"key": 978307200000,
"doc_count": 1
},
{
"key_as_string": "2002-01-01",
"key": 1009843200000,
"doc_count": 0
},
{
"key_as_string": "2003-01-01",
"key": 1041379200000,
"doc_count": 1
},
{
"key_as_string": "2004-01-01",
"key": 1072915200000,
"doc_count": 0
},
{
"key_as_string": "2005-01-01",
"key": 1104537600000,
"doc_count": 0
},
{
"key_as_string": "2006-01-01",
"key": 1136073600000,
"doc_count": 0
},
{
"key_as_string": "2007-01-01",
"key": 1167609600000,
"doc_count": 0
},
{
"key_as_string": "2008-01-01",
"key": 1199145600000,
"doc_count": 0
},
{
"key_as_string": "2009-01-01",
"key": 1230768000000,
"doc_count": 0
},
{
"key_as_string": "2010-01-01",
"key": 1262304000000,
"doc_count": 0
},
{
"key_as_string": "2011-01-01",
"key": 1293840000000,
"doc_count": 0
},
{
"key_as_string": "2012-01-01",
"key": 1325376000000,
"doc_count": 0
},
{
"key_as_string": "2013-01-01",
"key": 1356998400000,
"doc_count": 0
},
{
"key_as_string": "2014-01-01",
"key": 1388534400000,
"doc_count": 0
},
{
"key_as_string": "2015-01-01",
"key": 1420070400000,
"doc_count": 0
},
{
"key_as_string": "2016-01-01",
"key": 1451606400000,
"doc_count": 1
},
{
"key_as_string": "2017-01-01",
"key": 1483228800000,
"doc_count": 1
},
{
"key_as_string": "2018-01-01",
"key": 1514764800000,
"doc_count": 1
}
]
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
嵌套使用
聚合操作是可以嵌套使用的。通过嵌套,可以使得metric类型的聚合操作作用在每一bucket上。我们可以使用ES的嵌套聚合操作来完成稍微复杂一点的统计功能。
如:统计每年中用户的最高工资
POST /zpark/user/_search
{
"aggs": {
"date_histogram": { # bucket聚合 按照年分区
"date_histogram": {
"field": "birthday",
"interval": "year",
"format": "yyyy-MM-dd"
},
"aggs": {
"salary_max": {
"max": { # metric聚合 求最大工资
"field": "salary"
}
}
}
}
}
}
--------------------------------------------------------------------------------------
{
......
"aggregations": {
"date_histogram": {
"buckets": [
......
{
"key_as_string": "2017-01-01",
"key": 1483228800000,
"doc_count": 1,
"salary_max": {
"value": 5000
}
},
{
"key_as_string": "2018-01-01",
"key": 1514764800000,
"doc_count": 1,
"salary_max": {
"value": 1000
}
}
]
}
}
}
转载请注明:SuperIT » (重要)从零学Elasticsearch6.4系列——深入搜索(Query、Filter、Aggregation)