文章目录
ElasticSearch检索你的数据(一)
检索你的数据
检索查询是针对ElasticSearch上的数据流或者索引数据信息的一个请求。
你可以把查询看做是一个问题,以ElasticSearch理解的方式编写。基于你的数据,你可以使用查询来获取以下问题的答案:
- 我服务器上的哪些进的响应时间超过 500 毫秒?
- 上周我网络上的哪些用户运行了 regsvr32.exe?
- 我网站上的哪些页面包含特定的字词或短语?
一个检索包含一个或者更多合并在一起的查询并发送给ElasticSearch。匹配到的检索文档被返回到hits
中,或者检索结果中。
每一个检索也可能包含用于更好的处理其查询的额外信息。例如一个检索可能被限制在一个索引上或者返回特定数量的结果。
运行一个检索
你可以使用search API
检索和聚合存在ElasticSearch上的数据或者索引。这个API的query
请求体参数接收query DSL
编写的查询。
根据以下请求使用match
查询检索my-index-00001
。这个查询匹配user.id
的值为kimchy
的文档。
curl -X GET "localhost:9200/my-index-000001/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"user.id": "kimchy"
}
}
}
'
这个API响应返回匹配这个查询的前十条文档,
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.3862942,
"hits": [
{
"_index": "my-index-000001",
"_type": "_doc",
"_id": "kxWFcnMByiguvud1Z8vC",
"_score": 1.3862942,
"_source": {
"@timestamp": "2099-11-15T14:12:12",
"http": {
"request": {
"method": "get"
},
"response": {
"bytes": 1070000,
"status_code": 200
},
"version": "1.1"
},
"message": "GET /search HTTP/1.1 200 1070000",
"source": {
"ip": "127.0.0.1"
},
"user": {
"id": "kimchy"
}
}
}
]
}
}
定义只存在查询中的字段
你可以定义一个只存在于你检索查询部分中运行时字段,而不是索引你的数据然后检索他。你可以在查询请求中指定一个runtime_mapping
片段来定义运行时字段,这个运行时字段包含脚本是可选的。
例如根据查询定义一个名字为day_of_week
的运行时字段。其中包含了根据@timestamp
字段的值去计算星期几的脚本,并且是用emit
字段来接收计算值。
这个查询也包含了在day_of_week
上的terms aggregation
操作:
curl -X GET "localhost:9200/my-index-000001/_search?pretty" -H 'Content-Type: application/json' -d'
{
"runtime_mappings": {
"day_of_week": {
"type": "keyword",
"script": {
"source":
"emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))"
}
}
},
"aggs": {
"day_of_week": {
"terms": {
"field": "day_of_week"
}
}
}
}
'
响应包含了基于day_of_week
运行时字段的聚合值。buckets
中是一个值为Sunday
的key
。这个查询根据脚本中定义的运行时字段day_of_week
动态计算了星期几,而不是对该字段进行索引。
{
...
***
"aggregations" : {
"day_of_week" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Sunday",
"doc_count" : 5
}
]
}
}
}
通用的检索选项
你可以通过以下选项自定义你的检索
Query DSL
Query DSL支持多种检索类型,你可以搭配使用来获取你想要的结果。查询类型包含以下:
- 布尔查询和其他复合查询,可让您根据多个条件组合查询并匹配结果
- 用于过滤和查找完全匹配项的术语级查询
- 全文查询,常用于搜索引擎
- 地理和空间查询
Aggregations
你可以使用aggregate
检索来获取统计和其他分析的结果。Aggregation可以回到你一下问题:
- 我的服务器的平均响应时间
- 使用我们网络的前N个IP地址
- 客户的总交易收入是多少?
检索多个数据流和索引
您可以使用逗号分隔值和类似 grep 的索引模式在同一请求中搜索多个数据流和索引。您甚至可以提升特定索引的搜索结果。请参阅搜索多个数据流和索引。
分页你的结果
默认情况下,搜索仅返回前 10 个匹配的匹配项。要检索更多或更少的文档,请参阅分页搜索结果。
检索选定的字段
搜索响应的 hit.hits 属性包括每次命中的完整文档 _source。要仅检索 _source 或其他字段的子集,请参阅检索所选字段。
排序检索结果
默认情况下,搜索命中按 _score 排序,_score 是衡量每个文档与查询匹配程度的相关性分数。要自定义这些分数的计算,请使用 script_score 查询。要按其他字段值对搜索结果进行排序,请参阅对搜索结果进行排序。
运行异步查询
Elasticsearch 搜索旨在快速在大量数据上运行,通常在几毫秒内返回结果。因此,默认情况下搜索是同步的。搜索请求在返回响应之前等待完整的结果。
但是,对于多个集群的搜索,完整的结果可能需要更长的时间。
为避免长时间等待,您可以改为运行异步或异步搜索。异步搜索可让您现在检索长时间运行的搜索的部分结果,并在以后获得完整的结果。
检索超时
默认情况下,搜索请求不会超时。请求在返回响应之前等待来自每个分片的完整结果。
虽然异步搜索是为长时间运行的搜索而设计的,但您也可以使用 timeout 参数来指定您希望等待每个分片完成的持续时间。每个分片收集指定时间内的记录。如果时间结束时收集没有完成,ElasticSearch将只使用当前时间点命中的记录。检索请求的整体延迟取决于索引的分片数量和并发请求分片的数量。
curl -X GET "localhost:9200/my-index-000001/_search?pretty" -H 'Content-Type: application/json' -d'
{
"timeout": "2s",
"query": {
"match": {
"user.id": "kimchy"
}
}
}
'
为每一个请求设置集群范围内的超时时间,可以使用cluster settings API
设置search.default_search_timeout
。如果请求没有传递超时时间,则使用全局超时时间。如果全局超时时间在检索请求完成之前过期了,则这个请求将使用任务取消来取消。search.default_search_timeout
默认为-1
(没有超时时间)
检索取消
你可以使用task management APi
来取消检索请求。ElasticSearch也可以在你的客户端都是连接的时候自动取消检索请求。我们推荐你设置你的客户端在请求中止或者超时的时候关闭http连接。
跟踪总记录数
通常,如果不访问所有匹配项,就无法准确计算总命中数,这对于匹配大量文档的查询来说代价很高。track_total_hits
参数允许您控制如何跟踪总命中数。通常有一个命中数的下限就足够了,例如“至少有 10000 次命中”,默认设置为 10,000。这意味着请求将准确计算总命中数,最多为 10,000 次命中。如果在某个阈值之后不需要准确的命中数,那么加快搜索速度是一个很好的折衷方案。
当设置为 true 时,搜索响应将始终跟踪准确匹配查询的命中数(例如,当 track_total_hits
设置为 true
时,total.relation
将始终等于eq
)。否则在搜索响应的total
对象中返回total.relation
。gte
值表示total.value
是与查询匹配的总命中数的下限,eq
值表示total.value
是准确计数。
curl -X GET "localhost:9200/my-index-000001/_search?pretty" -H 'Content-Type: application/json' -d'
{
"track_total_hits": true,
"query": {
"match" : {
"user.id" : "elkbee"
}
}
}
'
返回:
{
"_shards": ...
"timed_out": false,
"took": 100,
"hits": {
"max_score": 1.0,
"total" : {
"value": 2048, //总记录数
"relation": "eq" //精确统计命中标识
},
"hits": ...
}
}
也可以将 track_total_hits
设置为整数。例如,以下查询将准确跟踪与查询匹配的总命中数最多 100 个文档:
curl -X GET "localhost:9200/my-index-000001/_search?pretty" -H 'Content-Type: application/json' -d'
{
"track_total_hits": 100,
"query": {
"match": {
"user.id": "elkbee"
}
}
}
'
返回:
{
"_shards": ...
"timed_out": false,
"took": 30,
"hits": {
"max_score": 1.0,
"total": {
"value": 42,
"relation": "eq"
},
"hits": ...
}
}
如果与查询匹配的总命中数大于 track_total_hits 中设置的值,则响应中的总命中数将指示返回值是下限:
{
"_shards": ...
"hits": {
"max_score": 1.0,
"total": {
"value": 100,
"relation": "gte"
},
"hits": ...
}
}
如果您根本不需要跟踪总匹配数,您可以通过将此选项设置为 false 来缩短查询时间:
curl -X GET "localhost:9200/my-index-000001/_search?pretty" -H 'Content-Type: application/json' -d'
{
"track_total_hits": false,
"query": {
"match": {
"user.id": "elkbee"
}
}
}
'
返回:
{
"_shards": ...
"timed_out": false,
"took": 10,
"hits": {
"max_score": 1.0,
"hits": ...
}
}
快速检查匹配文档
如果只想知道是否有任何文档匹配特定的查询,可以将size
设置为0,表示我们对搜索结果不感兴趣。还可以将terminate_after
设置为1,表示只要找到第一个匹配的文档(每个分片),就可以终止查询执行。
curl -X GET "localhost:9200/_search?q=user.id:elkbee&size=0&terminate_after=1&pretty"
当大小设置为0时,响应将不包含任何命中。hits.total
如果等于0,则说明没有匹配到任何文档。如果大于0,则说明在终止检索之前匹配了对应数量的文档。此外,如果查询提前终止,则在响应中将terminated_early
的标志为true
。
{
"took": 3,
"timed_out": false,
"terminated_early": true,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": null,
"hits": []
}
}
响应中took
的时间包含了处理该请求所花费的毫秒数,从节点收到查询开始,一直到所有搜索相关工作完成,并在将上述JSON返回给客户端之前。这意味着它包括在线程池中等待、跨整个集群执行分布式搜索和收集所有结果所花费的时间。
过滤检索结果
你可以使用两种方法过滤检索结果:
- 使用带有
filter
子句的布尔查询。检索请求对搜索结果和聚合应用布尔过滤器。 - 使用检索API的
post_filter
参数。检索请求只对检索结果应用post_filter
,而不应用于aggregation
。你可以使用post filter根据结果集计算聚合,进一步缩小结果范围。
Post Filter
当您使用post_filter
对检索结果进行过滤的时候,过滤的是聚合之后的结果。该过滤器不会对聚合结果产生影响。
例如,您正在销售具有以下属性的衬衫:
curl -X PUT "localhost:9200/shirts?pretty" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"brand": { "type": "keyword"},
"color": { "type": "keyword"},
"model": { "type": "keyword"}
}
}
}
'
curl -X PUT "localhost:9200/shirts/_doc/1?refresh&pretty" -H 'Content-Type: application/json' -d'
{
"brand": "gucci",
"color": "red",
"model": "slim"
}
'
假设用户指定了两个过滤器:
color:red
and brand:gucci
。你只想在检索结果中展示由Gucci制造的红色衬衫。通常你会用bool查询来做到这一点:
curl -X GET "localhost:9200/shirts/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"filter": [
{ "term": { "color": "red" }},
{ "term": { "brand": "gucci" }}
]
}
}
}
'
但是,您还希望使用分面导航来显示用户可以单击的其他选项列表。也许您有一个模型字段,允许用户将他们的搜索结果限制为红色Gucci t恤或正装衬衫。
这可以通过术语聚合来完成:
curl -X GET "localhost:9200/shirts/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"filter": [
{ "term": { "color": "red" }},
{ "term": { "brand": "gucci" }}
]
}
},
"aggs": {
"models": {
"terms": { "field": "model" }
}
}
}
'