Elasticsearch DSL查询入门
查询(query)与过滤(filter)
Elasticsearch中的数据检索分为两种情况:查询和过滤。
查询(Query)会对检索结果进行评分,注重的点是匹配程度,例如检索“运维咖啡吧”与文档的标题有多匹配,计算的是查询与文档的相关程度,计算完成之后会算出一个评分,记录在_score字段中,并最终按照_score字段来对所有检索到的文档进行排序。
过滤(Filter)不会对检索结果进行评分,注重的点是是否匹配,例如检索“运维咖啡吧”是否匹配文档的标题,结果只有匹配或者不匹配,因为只是对结果进行简单的匹配,所以计算起来也非常快,并且过滤的结果会被缓存到内存中,性能要比Query查询高很多。
简单查询例子
POST /my_index_name/_search
{
"query":{
"match_all": {}
}
}
/_search 查找整个ES中所有索引的内容。
query 为查询关键字,类似的还有aggs为聚合关键字。
match_all 匹配所有的文档,也可以写match_none不匹配任何文档。
返回结果如下
{
"took": 6729,
"timed_out": false,
"num_reduce_phases": 6,
"_shards": {
"total": 2611,
"successful": 2611,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 7662397664,
"max_score": 1,
"hits": [
{
"_index": ".kibana",
"_type": "doc",
"_id": "url:ec540365d822e8955cf2fa085db189c2",
"_score": 1,
"_source": {
"type": "url",
"updated_at": "2018-05-09T07:19:46.075Z",
"url": {
"url": "/app/kibana",
"accessCount": 0,
"createDate": "2018-05-09T07:19:46.075Z",
"accessDate": "2018-05-09T07:19:46.075Z"
}
}
},
...省略其他的结果...
]
}
}
took: 表示我们执行整个搜索请求消耗了多少毫秒
timed_out: 表示本次查询是否超时
这里需要注意当timed_out为True时也会返回结果,这个结果是在请求超时时ES已经获取到的数据,所以返回的这个数据可能不完整。
且当你收到timed_out为True之后,虽然这个连接已经关闭,但在后台这个查询并没有结束,而是会继续执行
_shards: 显示查询中参与的分片信息,成功多少分片失败多少分片等
hits: 匹配到的文档的信息,其中total表示匹配到的文档总数,max_score为文档中所有_score的最大值
hits中的hits数组为查询到的文档结果,默认包含查询结果的前十个文档,每个文档都包含文档的_index、_type、_id、_score和_source数据
结果文档默认情况下是按照相关度(_score)进行降序排列,也就是说最先返回的是相关度最高的文档,文档相关度意思是文档内容与查询条件的匹配程度,上边的查询与过滤中有介绍
分页查询
上边有说到查询结果hits默认只展示10个文档,那我们如何查询10个以后的文档呢?ES中给了size和from两个参数
size: 设置一次返回的结果数量,也就是hits中的文档数量,默认为10
from: 设置从第几个结果开始往后查询,默认值为0
POST /my_index_name/_search
{
"size": 5,
"from": 10,
"query":{
"match_all": {}
}
}
全文查询
上边有用到一个match_all的全文查询关键字,match_all为查询所有记录,常用的查询关键字在ES中还有以下几个
match
模糊匹配,最简单的查询,会把查询条件进行分词,or 关系,多个词条之间是 or 的关系,下边的例子就表示查出car包含奥迪A8L 和 奥迪 和 A8L 都查询出来,按照词进行查询
POST /my_index_name/_search
{
"query": {
"match": {
"car": "奥迪A8L"
}
}
}
match_phrase
短语匹配:如 搜索"奥迪A8L" 不会查出包含 "奥迪" OR "A8L" 只会查出包含 "奥迪A8L"的记录
POST /my_index_name/_search
{
"query": {
"match_phrase": {
"car": "奥迪A8L"
}
}
}
multi_match
在多个字段上执行相同的match查询,下边的例子就表示查询host或http_referer字段中包含ops-coffee.cn的记录
POST /my_index_nname/_search
{
"query":{
"multi_match": {
"query":"ops-coffee.cn",
"fields":["host","http_referer"]
}
}
}
query_string
可以在查询里边使用AND或者OR来完成复杂的查询,例如:
POST /my_index_name/_search
{
"query":{
"query_string": {
"query":"(a.ops-coffee.cn) OR (b.ops-coffee.cn)",
"fields":["host"]
}
}
}
以上表示查找host为a.ops-coffee.cn或者b.ops-coffee.cn的所有记录
也可以用下边这种方式组合更多的条件完成更复杂的查询请求
POST /my_index_name/_search
{
"query":{
"query_string": {
"query":"host:a.ops-coffee.cn OR (host:b.ops-coffee.cn AND status:403)"
}
}
}
term
将按照存储在倒排索引中的确切字词进行操作,这些查询通常用于数字,日期和枚举或者是设置了not_analyzed不分词的字符串等结构化数据,而不是全文本字段。 或者,它们允许您制作低级查询,并在分析过程之前进行
POST /my_index_name/_search
{
"query":{
"term": {
"status": {
"value": 404
}
}
}
}
term对输入的文本不进行分析,直接精确匹配输出结果,如果要同时匹配多个值可以使用terms
POST /my_index_name/_search
{
"query": {
"terms": {
"status":[403,404]
}
}
}
range
range用来查询落在指定区间内的数字或者时间
POST /my_index_name/_search
{
"query": {
"range":{
"status":{
"gte": 400,
"lte": 599
}
}
}
}
以上表示搜索所有状态为400到599之间的数据,这里的操作符主要有四个gt大于,gte大于等于,lt小于,lte小于等于
当使用日期作为范围查询时,我们需要注意下日期的格式,官方支持的日期格式主要有两种
- 时间戳,注意是毫秒粒度
POST /my_index_name/_search
{
"query": {
"range": {
"@timestamp": {
"gte": 1557676800000,
"lte": 1557680400000,
"format":"epoch_millis"
}
}
}
}
- 日期字符串
POST /my_index_name/_search
{
"query": {
"range":{
"@timestamp":{
"gte": "2019-05-13 18:30:00",
"lte": "2019-05-14",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd",
"time_zone": "+08:00"
}
}
}
}
通常更推荐用这种日期字符串的方式,看起来比较清晰,日期格式可以按照自己的习惯输入,只需要format字段指定匹配的格式,如果格式有多个就用||分开,像例子中那样,不过我更推荐用同样的日期格式
如果日期中缺少年月日这些内容,那么缺少的部分会用unix的开始时间(即1970年1月1日)填充,当你将"format":"dd"指定为格式时,那么"gte":10将被转换成1970-01-10T00:00:00.000Z
elasticsearch中默认使用的是UTC时间,所以我们在使用时要通过time_zone来设置好时区,以免出错
组合查询
通常我们可能需要将很多个条件组合在一起查出最后的结果,这个时候就需要使用ES提供的bool来实现了
例如我们要查询host为ops-coffee.cn且http_x_forworded_for为111.18.78.128且status不为200的所有数据就可以使用下边的语句
POST /my_index_name/_search
{
"query":{
"bool": {
"filter": [
{"match": {"host": "ops-coffee.cn"}},
{"match": {"http_x_forwarded_for": "111.18.78.128"}}
],
"must_not": {"match": {"status": 200}}
}
}
}
主要有四个关键字来组合查询之间的关系,分别为:
must: 查询必须同时满足我所有条件,类似于SQL中的AND,必须包含
must_not: 查询不满足条件,类似于SQL中的NOT,必须不包含
should: 至少满足一个条件,满足这些条件中的任何条件都会增加评分_score,不满足也不影响,should只会影响查询结果的_score值,并不会影响结果的内容
filter: 与must相似,但不会对结果进行相关性评分_score,大多数情况下我们对于日志的需求都无相关性的要求,所以建议查询的过程中多用filter
聚合查询
group by
#根据年龄字段分组查询
POST /my_index_name/_search
{
"aggs": {
"group_by_age": {
"terms": {
"field": "age"
}
}
}
}
#根据条件分组
POST /my_index_name/_search
{
"query": {
"match": {
"car": "奥迪"
}
},
"aggs": {
"group_by_age": {
"terms": { "field": "age" }
}
}
}
#分组并求平均值
POST /my_index_name/_search
{
"aggs": {
"group_by_age": {
"terms": { "field": "age" },
"aggs" : {
"avg_age" : {
"avg" : { "field" : "age" }
}
}
}
}
}
参考
https://cloud.tencent.com/developer/article/1694119
https://cloud.tencent.com/developer/article/2333857
https://cloud.tencent.com/developer/article/1562429