认识ElasticSearch的API,并深入Search的使用

0.引言

本文罗列介绍了ES提供的公共API,重点围绕数据检索主题相关API进行说明总结。

1.概述

Elasticsearch提供全功能的RESTful API。以基于HTTP协议传输交换JSON数据的方式,向用户提供访问服务。

具体的访问方式可按照参数的提交方法区分为以下两种:

  1. 通过URI参数提交,比如:
curl 'localhost:9200/bank/_search?q=*&pretty
  1. 通过请求报文体提交,比如:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": { "match_all": {} }
}'

虽说以上说明中的两个例子效果一致,但是具体来说,报文体提交的方式具备更高的可读性,并且具备更丰富的表示能力,有一些功能是必须使用该方式才可以被访问。URI参数方式的优势在于便于快速测试。

有很重要的一点必须要理解的是:一旦请求结果返回,那么Elasticsearch就完全结束了关于该次请求的处理,不会维护任何跟查询结果相关的类似于“游标”概念的服务器端资源,查询结果中当然也不会出现该类资源的引用。这种方式跟其他诸如SQL平台的处理方法截然不同。通常在SQL平台中用户会得到查询结果集中靠前的部分子集,之后如果再有需要,用户可以通过能够维护访问状态的“游标”机制继续向服务器获取查询结果集中的剩余部分。

关于ES提供的数据存取服务,可以按照访问对象类型、操作类型的种类,将所有的API作以梳理划分。那么这些划分,在API技术层面则有可能分别对应着不同种类的URL访问点、HTTP方法、网络请求参数等等。

2.基本分类

就ES API大方面的功能语义而言,可以分为以下几类:

输入图片说明

  1. Document API:文档的增、删、改、查(按文档ID)
  2. Search API:文档搜索
  3. Aggregation API:聚合计算
  4. Indices API:索引操作,创建、查看、删除、打开、关闭等等
  5. Cat API:可以获得压缩与对齐结果的API(遵循传统的终端命令结果形式,非JSON响应格式)
  6. Cluster API:集群管理操作

3.Document API

Document API中主要以HTTP中PUT、DELETE、POST、GET这四种方法,来对应到文档的增、删、改、查四种操作,而服务访问点就以<server>/{<index-name>}/{<type-name>}为模式;HTTP方法与URL结合使用,以达到文档操作目的。

而且ES还提供了关于该主题的批量操作API,分别是批量查询和批量新增。批量查询为GET <server>/{<index-name>}/{<type-name>}/_mget;批量新增为POST <server>/{<index-name>}/{<type-name>}/_bulk

输入图片说明

4.Search API

4.1 Search概述

使用Search API可以执行查询,得到匹配查询的检索结果。关于查询本身,就像前文概述中所说,可以通过URL参数或者请求报文体两种方式来提交。所有的Search API都可以跨多个索引,以及在一个索引内跨多种Type。关于这一点,体现在API的访问点URL上,就是以下情况:

GET <server>/{<index-name-list>}/{<type-name-list>}/_search

举例如下:

检索范围为twitter索引中所有type:

$ curl -XGET 'http://localhost:9200/twitter/_search?q=user:kimchy'

检索范围为twitter索引中,tweet和user这两种type:

$ curl -XGET 'http://localhost:9200/twitter/tweet,user/_search?q=user:kimchy'

检索范围为kimchy与elasticsearch两个索引中tweet这种type:

$ curl -XGET 'http://localhost:9200/kimchy,elasticsearch/tweet/_search?q=tag:wow'

检索范围为所有available的索引中tweet这种type:

$ curl -XGET 'http://localhost:9200/_all/tweet/_search?q=tag:wow'

检索范围为所有索引中的所有type:

$ curl -XGET 'http://localhost:9200/_search?q=tag:wow'

4.2 Search API类别细分

Search本身涵盖了较大的概念范围,比如“搜索匹配检索条件的文档集合”、“获得匹配检索条件的文档数量”、“检查是否存在符合检索条件的文档”,等等,这些都属于搜索的范畴。这些不同的API语义差别,对应了不同的API访问点。以下对Search API做了类别细分:

输入图片说明

4.2.1 /_search

该访问点是数据搜索中最常用的API。接下来将按照报文体提交搜索的方式来详细说明。

$ curl -XGET 'http://localhost:9200/twitter/tweet/_search' -d '{
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}
'

可以看到,报文体中提交了一个字段名为query用以表示查询条件的字段信息。该字段也是整个检索查询中最重要的提交信息,但是检索查询中还可以提交更多的查询相关参数。

参数名称 描述
timeout 限定执行过期时长。缺省不设此值,不设立超时限制。
from 返回文档结果起始位置。缺省为0
size 返回文档结果集大小。缺省为10
search_type dfs_query_then_fetch or query_then_fetch . Defaults to query_then_fetch. See Search Type for more.
request_cache true 或者 false。当检索操作不返回文档集本身的情况下,设置是否缓存。See Shard request
terminate_after 限定从每个shard中收集的最大文档数量。缺省不设此最大数量限制。如果设定了该值,那么响应结果中将会包含一个terminated_early的字段,以表示该次查询实际上是较早地终止了。
sort 通过设置一个(或一组)排序字段来控制结果集中的文档顺序。
_source 控制、过滤结果中的_source字段。默认情况下,文档搜索操作返回的内容中包括文档中的_source字段,除非使用_source参数或者fields参数进行控制。See Source filtering
fields fields参数描述的是关于在类型映射定义中被明确指定存储的字段。指定被存储的字段中,哪些是需要被加载返回的。See fields
script_fields 允许使用脚本定义新的字段。
fielddata_fields Allows to return the field data representation of a field for each hit。
post_filter 应用在检索结果集处理的最末尾处。其目的在于做聚合统计时针对更多范围内数据,但是返回的检索查询结果集中只需要其中一部分即可。那么,post_filter就是用于最后过滤出想要的那一小部分。
highlight Allows to highlight search results on one or more fields。See Highlighting
rescore 设置重新计算相关度得分的机制。该机制使得可以针对排名较前的特定范围内的检索结果(在querypost_filter之后),进行重新排序。(并非针对所有的检索结果都重新计算分数)
scroll 使用该参数,可以使得用户可以像使用传统数据库游标一样来获取较大的查询结果集,而不再限于一次性的“单页”结果。
preference 可以使用该参数控制检索请求执行在哪个分片上。缺省情况下是随机的。
explain true 或者 false,控制是否解释检索结果中每个文档的得分是如何计算出来的。
version true 或者 false, 控制是否返回每个检索结果项的版本号码。
indices_boost 使用该参数可以针对各个索引来配置不同的评分基数。这种设置对于明确知晓某索引中的数据相对比较重要的情况是很有用的。
min_score 设置得分最低阈值,用于排除掉结果集中低于该分数的文档。

关于参数传递

以上参数中,除了search_typerequest_cache是必须通过URL参数来传递,其它的都建议置于请求报文体中。不过请求报文体的内容本身,也可以通过一个名为source的URL参数来传递。

HTTP GET 和 POST 都可以被用来发送请求。(主要是因为并不是所有的http client都能够允许GET方法发送报文体,所以POST方法是被允许的。)

关于/search的响应内容

关于/search的响应内容,除了包含匹配搜索条件的文档之外,还包含了跟检索结果相关的检索性能信息反馈。举例如下:

{ 
  "took" : 2,                       #A 
  "timed_out" : false,              #A 
  "_shards" : { 
    "total" : 2,                    #B 
    "successful" : 2,               #B 
    "failed" : 0                    #B 
  }, 
  "hits" : { 
    "total" : 2,                    #C 
    "max_score" : 0.9066504,        #C 
    "hits" : [ {                    #D 
      "_index" : "get-together",    #D 
      "_type" : "group",            #D 
      "_id" : "3",                  #D 
      "_score" : 0.9066504,         #D 
      "fields" : {                  #D 
        "location" : "San Francisco, California, USA",    #D 
        "name" : "Elasticsearch San Francisco"            #D 
      }                                                   #D 
    } ]                                                   #D 
  } 
} 
  • A: How long your request took and if it timed out
  • B: How many shards were queried
  • C: Statistics on all documents that matched
  • D: The results array

A.Time

响应结果中最先展示的信息就是关于该次请求的耗时信息:

 "took" : 2, 
 "timed_out" : false, 

took字段显示了ES花费了多少时间来处理该次请求(毫秒单位)。time_out字段显示该次请求处理是否超时。缺省情况下是不会出现超时的,除非在查询提交时设定了timeout参数。如若出现超时情形,那么返回的检索结果仅仅是超时前能够收集到的部分结果。

B.Shards

响应结果中其次展示的信息就是关于该次请求涉及到的分片信息:

 "_shards" : { 
    "total" : 5, 
    "successful" : 5, 
    "failed" : 0 
 } 

展示了总共有多少个分片被分派了该次请求,其中成功和失败各有多少次。

C.Hits statistics

响应结果中最后展示的就是关于检索查询的命中文档信息了。hits元素有可能会比较相当长,因为它包含了一个所有匹配文档的数组。不过,在文档数组之前它还包括了一组统计信息:

 "total" : 2, 
 "max_score" : 0.90178301 

total数值不一定会和返回结果中的数组长度一致,一般情形下都会远大于实际返回的数组长度。缺省情况下,ES限制了实际返回结果长度为10。而total则表示了索引中匹配检索的文档总数目。可以通过提交查询时的size参数来控制实际返回结果大小。

D.Resulting documentss

检索结果文档集,会以数组形式出现在hits字段上。

    "hits" : [ { 
      "_index" : "get-together", 
      "_type" : "group", 
      "_id" : "3", 
      "_score" : 0.9066504, 
      "fields" : { 
        "location" : "San Francisco, California, USA", 
        "name" : "Elasticsearch San Francisco" 
      } 
    } ] 

每个匹配的文档都会显示其归属的索引和类型,以及文档ID和得分。如果在提交查询时定义了 “字段”列表(fields属性),那么文档内容将展示相关字段信息。如果在提交查询时没有设置关于fields的设定,那么将会显示_source内容。

关于searchType

执行一次分布式检索可以通过许多种不同的执行路径。分布式检索操作,需要将检索请求分发到所有相关的分片上,然后在收集所有的检索结果。当做这种“分发/收集”操作时,可以有多种方式,尤其是对于搜索引擎而言。这个过程中可能会涉及以下问题:

问题1:执行分布式搜索时,需要在每个分片上获取多大的结果集呢?

举个例子,如果当前有10个分片,我们需要从中检索出10个满足检索条件的文档,那么就需要从所有的分片中各检索10个文档,然后将他们排序,确保最终返回正确的结果。

问题2:每个分片只能局限于自身所保存的信息来执行检索操作,它不能够考虑到其它索引中保存的类似于词频等搜索引擎信息。

举个例子,如果我们需要获取准确的排序结果,那么将需要首先从所有的分片中收集词频信息并进行全局的计算,然后再在各个分片上以全局的词频信息来执行检索操作。

ES允许用户可以通过请求参数来控制检索请求的执行方式,通过search_type进行设定即可。

query_then_fetch

该类型设定为系统缺省设置。在该设定下,检索请求将分为两阶段进行。

1.第一阶段:查询请求被转发给所有被涉及的分片,每个分片在本地执行检索并产生有序结果。然后,每个分片仅返回足够分发节点合并、重排各分片结果的信息,使得分发节点初步得到一个全局排序的结果集,但是该结果集中并不包括命中文档本身。

2.第二阶段:分发节点从相关分片中获取结果集文档内容。

dfs_query_then_fetch

query_tehn_fetch基本相同,区别在于第一阶段中,各分片中执行检索时依据的评分信息被换做了全局的term信息,以期能够得到更精确的评分结果。

count

首先:Deprecated in 2.0.0-beta1. Because count does not provide any benefits over query_then_fetch with a size of 0.

其次:通常来讲,直接使用 /count API 会更好一点,因为那样会有更多的设置选项可供使用。

scan

首先:Deprecated in 2.1.0. Because scan does not provide any benefits over a regular scroll request sorted by _doc.

scan类型检索的初衷是为了通过关闭排序功能,进而允许可以以较高的效率来滚动读取较大的结果集。但是,从版本2.1.0开始,更推荐直接使用/scroll API。

关于_source与fields以及script fields

ES本身提供了若干预定义字段,用于完成一些关键且必要的功能。这些字段一般都具备有以下特征:

  • 名称以下划线"_"开头
  • 用户不需要直接去声明其定义,也不需要直接为其赋值
  • 这些字段往往起到一些关键性作用

_source就是其中之一。该字段的目的是为了存储文档的原始文本内容的。有了它,才会使得检索后的结果不仅仅只是一个文档ID,而且可以直接看到匹配检索的文档内容。

在创建type的时候可以特意指定关闭_source字段内容,但是该功能默认是开启的。在检索时,返回结果默认也是会带回_source内容的,该行为可以通过在提交检索请求时设置_source参数为false来关闭。

另外,在检索文档时,不仅仅可以简单地控制返回结果中是否包括_source内容,而且可以更精细地控制需要具体哪些字段。方法为在检索查询参数中使用_source参数过滤或者fields参数,只是二者有所区别。

使用_source过滤的方法如下:

{
    "_source": "obj.*",
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

使用该参数时,查询结果中将不再返回_source中的所有内容,而是有选择性地返回指定字段。

使用fields属性也可以起到过滤返回字段的效果。但是它与_source属性的机制不太一样。fields属性原本是针对被特殊指定须存储的字段内容,只是缺省情况下,字段是不会被存储的。关于字段是否须被存储,其设定方式是在创建type定义时,与指定字段类型一样。

不过,当前情况下,ES考虑到向后兼容性,就算是fields指定的字段并没有被存储,那么使用fields的实现方式就自动变成了“先加载_source,然后再解析字段内容”。

上段中提到了两种存储:_source与个别独立字段。这二者没有太大联系,可以同时都存储,也可以同时都不存储,当然也可以只存储其中一种。二者的应用方面差别在于,如果存在经常需要访问某一个特定字段的情形,那么单独存储该字段将会比从_source表示的整个文档内容中解析的效率高。当然,存储将会引发空间代价,不过ES会对_source和其它任何独立字段的内容进行压缩。

最后再说到script_fields,使用该提交参数可以运用脚本能力来生成新的临时字段。基本过程就是使用脚本表达式,来基于原有的文档信息来生成新字段,比如下面的例子:

{
    "query" : {
        ...
    },
    "script_fields" : {
        "test1" : {
            "script" : "doc['my_field_name'].value * 2"
        },
        "test2" : {
            "script" : {
                "inline": "doc['my_field_name'].value * factor",
                "params" : {
                    "factor"  : 2.0
                }
            }
        }
    }
}

该项功能的使用具体细节可多参照脚本使用方面的参考。

关于字段_all

就像_source字段存储了所有信息一样,_all字段索引了所有信息。当在_all字段上进行检索时,只要有任意字段能够匹配,那么ES都会返回相关结果。这种操作在用户不清楚具体目标字段时比较有用。以下检索就是在_all上进行的:

curl 'localhost:9200/get-together/group/_search?q=elasticsearch' 

假如对检索功能没有检索任意字段的需求,那么在创建type时可以像关闭_source一样来关闭_all。这样会减小索引体积,并且加快索引速度。

另外,还可以针对字段级别来设定当前字段是否包含在_all中,默认都是包含的除非指定字段属性include_in_allfalse

关于排序

在提交检索查询时,可以通过设置sort参数来控制返回指定排序结果。sort定义是基于一个或多个字段的,指定这些字段的升降序和排序优先级。所以sort本身将会是一个复合对象,举个例子如下:

{
    "sort" : [
        { "post_date" : {"order" : "asc"}},
        "user",
        { "name" : "desc" },
        { "age" : "desc" },
        "_score"
    ],
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

除了_score字段的默认顺序是desc之外,其余的任何字段默认顺序都是asc。排序字段除了可以是文档字段,还有两个特殊的内置字段可以被指定为排序字段:

1._score:文档相关度评分。 2._doc:文档被索引的顺序。该顺序几乎没有实际需求场景,只是其优势在于排序效率最高。所以,一般在不关心结果顺序时应当使用_doc排序。尤其是在使用scroll操作时。

排序内存耗用

在排序时,相关的排序字段值都会被载入内存。这意味着每个分片都需要足够的内存来能够容下这些数据。所以,对于字符串类型的排序字段应当避免被分析;数值类型字段应当尽量设置为较短长度(比如short integer float 而不是 long double)。

其它关于排序的内容,还有诸如:

  • 如何使用多值字段来排序
  • 如何处理缺失字段的情况
  • 如何根据地理位置距离来排序
  • 如何使用排序脚本

等等,可查看更多参考说明

关于分页

from + size

前文已经提到ES并不像传统的数据库那样能够提供“游标”来分步、连续获得查询结果集。所以,关于ES查询分页的问题,最简陋的做法就是直接使用查询请求中的fromsize参数来进行控制。

  • from:定义了获取结果中首元素的偏移量。
  • size:定义了获取结果的最大文档数量。

比如下例请求,得到第0-9个,共10个结果:

{
    "from" : 0, "size" : 10,
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

接下来可以通过第二次请求来获取第10-19个元素,为第二页结果:

{
    "from" : 10, "size" : 10,
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

不过需要注意的是,from+size是不允许超出ES配置项index.max_result_window的。该配置项默认值为10000。其背后的含义也就是,该方法其实是每次都从头获取、截断返回的做法。一来效率不过,二来无法进行更深的分页。

scroll

关于分页,更专业的做法是使用scroll相关的API。

scroll为ES的查询模式开设一个特例,它打破了通常ES查询的“一锤子”模式,提供了类似于数据游标机制。使得用户有机会能够获取一次查询相关的大批量结果数据甚至是全量结果数据。

相对于面向终端用户的实时查询,scroll更适用于大批量数据处理。比如,索引的迁移:将旧索引中的数据迁出到一个配置变更后的新索引中。

scroll API的具体使用方式举例说明如下:

在提交查询请求时,使用scroll参数来(1)指定本次请求是需要创建scroll search上下文的;(2)指定该scroll search上下文的存活过期时长为多久。

curl -XGET 'localhost:9200/twitter/tweet/_search?scroll=1m' -d '
{
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    }
}
'

以上请求返回的查询结果相对于普通查询特殊的一点是结果中会包括一个_scroll_id字段。那么接着获取下一批数据结果时,直接使用该字段即可:

curl -XGET  'localhost:9200/_search/scroll'  -d'
{
    "scroll" : "1m", 
    "scroll_id" : "c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1" 
}
'

对于后续的请求发送,URL中不必再包括indextype名称以及其他检索相关的信息,这些都在初始检索查询时已经确定。不过继续使用scroll参数来更新对应上下文的存活过期时长。该请求的关键在于scroll_id参数。每次执行scroll查询都会获得下一批数据结果直到没有剩余结果(hit数组为空)。

老的ES版本中,发送scroll_id的方法为直接将id值写在报文体中:

curl -XGET 'localhost:9200/_search/scroll?scroll=1m' -d 'c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1'

在使用scrollAPI时,有以下需要注意的几点:

  1. ES官方文档中提示,每次scroll请求都会返回关联的_scroll_id,并且还提示每次返回的_scroll_id可能会有所更新,对于用户而言应该使用最近一次返回的_scroll_id。不过,笔者实验的ES版本并没有在后续请求过程中更新_scroll_id,而是一直沿用首次请求创建的_scroll_id值。Whatever,追踪最新的_scroll_id应该是没错的,无论该值是否会被更新。
  2. 如果检索请求中定义了聚合计算,那么只有初次请求的返回结果中会包含聚合结果。
  3. 对于scroll检索,设置排序方式为按_doc字段,会对请求的执行起到优化效果。比如:
curl -XGET 'localhost:9200/_search?scroll=1m' -d '
{
  "sort": [
    "_doc"
  ]
}
'

scroll检索上下文的存活时间

在进行scroll请求时,无论是初次请求还是后续请求,都需要使用scroll参数来指定ES保存检索上下文的过期时长。比如设置scroll=1m。(时间单位说明)该过期时长的意义是针对请求者处理而言的,指的是请求者在这么长时间范围内会处理完该批数据,然后再发起下一批的数据请求。如果超时,那么ES将放弃保存该次请求的上下文以免不必要的资源耗用。

这背后的影响在于,ES会周期性地将小的索引合并为大的索引进而优化检索效率,合并完成后将会删除掉旧的小索引。scroll 检索上下文将会避免ES删除掉正在使用中的小索引(使用完毕后再删除)。换个角度来看,scroll请求返回的数据反映的是其搜索请求被创建时的数据状态,就好比是当时的数据快照。那么后续如果有文档数据变动,只会影响到下一次的请求,对已经在执行的scroll检索无影响。

检查当前“scroll 检索上下文的存活情况”可以使用以下方法:

curl -XGET localhost:9200/_nodes/stats/indices/search?pretty

关于scroll的销毁清理

就像之前所说的那样,srcoll检索上下文将会在超时后被自动清理。保持scroll检索上下文具有一定的代价,所以做到尽快清理是十分必要的,比如明确scroll检索已经执行完毕的时候应该立即主动关闭其检索上下文。关闭方法如下,使用clear-scrollAPI即可:

curl -XDELETE localhost:9200/_search/scroll -d '
{
    "scroll_id" : ["c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1"]
}'

一次性关闭多组:

curl -XDELETE localhost:9200/_search/scroll -d '
{
    "scroll_id" : ["c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1", "aGVuRmV0Y2g7NTsxOnkxaDZ"]
}'

一次性关闭所有:

curl -XDELETE localhost:9200/_search/scroll/_all

关于命名查询

在进行检索查询时,每个filterquery都可以接收一个_name参数设定。如下:

{
    "size": 5,
    "query": {
        "bool": {
            "must": {
                "terms": {
                    "_name": "test",
                    "direct": [
                        "process_send"
                    ]
                }
            }
        }
    }
}

返回的结果文档中将会包含matched_queries信息:

{
      "_index" : "main",
      "_type" : "pix0",
      "_id" : "AVNA6II_Kawvxx13OgK4",
      "_score" : 3.4127,
      "matched_queries" : [ "test" ]
}

4.2.2 /_search/template

/_search/template访问点是对/_search的模板化扩展,它在原功能基础上引入了模板化机制,使得一些具有固定检索条件结构的应用场景可以使用模板来达到容易维护的目的。

具体方式采用mustach语言来进行模板定义。举例如下:

GET /_search/template
{
    "inline" : {
      "query": { "match" : { "{{my_field}}" : "{{my_value}}" } },
      "size" : "{{my_size}}"
    },
    "params" : {
        "my_field" : "foo",
        "my_value" : "bar",
        "my_size" : 5
    }
}

更多关于mustache,请参照online documentation of the mustache project

模板本身可以在系统中进行预定义,比如以*.mustache文件的方式置于系统的config/scripts目录中;或者保存在ES中的一个命名为.scripts的特殊索引中。在使用这些内置模板的时候,只需要使用文件名(脚本文件)或者模板名称(索引存储)来引用即可。比如下例所示:

GET /_search/template
{
    "file": "storedTemplate", 
    "params": {
        "query_string": "search for these words"
    }
}

还需要说明的是,为了方便模板的调试,ES提供/_render/template访问点用于模板校验。如下例:

GET /_render/template
{
  "inline": {
    "query": {
      "terms": {
        "status": [
          "{{#status}}",
          "{{.}}",
          "{{/status}}"
        ]
      }
    }
  },
  "params": {
    "status": [ "pending", "published" ]
  }
}

返回模板渲染后的结果:

{
  "template_output": {
    "query": {
      "terms": {
        "status": [ 
          "pending",
          "published"
        ]
      }
    }
  }
}

这种校验测试方式同样适用于预定义的模板,比如下例所示:

GET /_render/template
{
  "file": "my_template",
  "params": {
    "status": [ "pending", "published" ]
  }
}

4.2.3 /_suggest

对于搜索引擎用户而言,当其使用搜索功能开始键入检索关键字时,搜索引擎往往会依据用户输入来及时的不断地提示相似的、相关的搜索内容,进而帮助用户更好地选定更精确的检索关键词。那么访问点/_suggest就是提供该方面功能的,比如下例:

curl -XPOST 'localhost:9200/_suggest' -d '{
  "my-suggestion" : {
    "text" : "the amsterdma meetpu",
    "term" : {
      "field" : "body"
    }
  }
}'

该功能本身也是基于检索来完成的,所以除了直接访问/_suggest之外呢还可以通过访问/_search来完成,只是需要带上相关请求参数即可:

curl -s -XPOST 'localhost:9200/_search' -d '{
  "query" : {
    ...
  },
  "suggest" : {
    ...
  }
}'

suggest特性本身还在部分完善中,更多参考请见Suggesters

4.2.4 /_msearch

ES提供了允许批量执行检索的API,其访问点就是/_msearch。发向该访问点的请求内容有点类似于使用 bulk API的情况,其内容结构如下:

header\n
body\n
header\n
body\n

其中header部分包括:

  • index信息
  • type信息
  • search_type信息
  • preference信息
  • routing信息

body部分包括通常search request中可以包含的内容,比如query aggregations from size 等等。

例如:

$ cat requests
{"index" : "test"}
{"query" : {"match_all" : {}}, "from" : 0, "size" : 10}
{"index" : "test", "search_type" : "dfs_query_then_fetch"}
{"query" : {"match_all" : {}}}
{}
{"query" : {"match_all" : {}}}

{"query" : {"match_all" : {}}}
{"search_type" : "dfs_query_then_fetch"}
{"query" : {"match_all" : {}}}

$ curl -XGET localhost:9200/_msearch --data-binary "@requests"; echo

上例中有两个header分别是{}和空,这两种情况是等价的(即遵循默认设定)。对于批量检索请求,响应结果将会是一个responses数组,按顺序对应提交批量检索。那如果其中有执行错误的结果,将会在responses中原本表示对应检索结果的位置上出现一个错误消息。

关于/_msearch的使用,也可以像之前介绍使用/_search那样,通过URL路径来控制访问的索引范围和类型范围,一旦这样做等于是为批量请求设定了默认的检索目标除非检索请求中在header部分覆写了有关设置。例如:

$ cat requests
{}
{"query" : {"match_all" : {}}, "from" : 0, "size" : 10}
{}
{"query" : {"match_all" : {}}}
{"index" : "test2"}
{"query" : {"match_all" : {}}}

$ curl -XGET localhost:9200/test/_msearch --data-binary @requests; echo

以上批量请求将会默认执行在索引test中(第一、第二这两个请求),但是最后一个请求由于进行了特殊设置所以将会执行在索引test2上。

除了检索范围,像search_type这样的控制参数也可以进行写在URL中进行全局的默认设定。

4.2.5 /_count

该API服务于用户只需得到匹配文档数量,而不关心具体文档内容的情况。对于该API的使用基本上等价于使用/_search时设定size参数为0的情形。所以对其的使用也基本与/_search一致,除了URL访问点地址的不同。

4.2.6 /_search/exists

该API服务于用户仅需要知晓对于目标查询是否有任何一个文档被命中的场景。其用法也与/_search基本相同。其响应结果大概如下:

{
    "exists" : true
}

只是在ES2.1.0版本之后,已经丢弃了该API。推荐直接使用/_search且设置参数size=0&terminate_after=1

4.2.7 /_validate

该API使得用户可以在检索查询执行之前,提前校验请求内容本身是否合法,而不需要真正地执行本次请求。使用方法如下:

curl -XPUT 'http://localhost:9200/twitter/tweet/1' -d '{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}'

如果,请求合法,那么响应结果如下:

curl -XGET 'http://localhost:9200/twitter/_validate/query?q=user:foo'
{"valid":true,"_shards":{"total":1,"successful":1,"failed":0}}

如果,请求非法,那么响应结果如下:

curl -XGET 'http://localhost:9200/twitter/tweet/_validate/query?q=post_date:foo'
{"valid":false,"_shards":{"total":1,"successful":1,"failed":0}}

在使用/_validate时还可以特别地附带上explain参数,进而可以得到更详细的校验结果:

curl -XGET 'http://localhost:9200/twitter/tweet/_validate/query?q=post_date:foo&pretty=true&explain=true'
{
  "valid" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "explanations" : [ {
    "index" : "twitter",
    "valid" : false,
    "error" : "[twitter] QueryParsingException[Failed to parse]; nested: IllegalArgumentException[Invalid format: \"foo\"];; java.lang.IllegalArgumentException: Invalid format: \"foo\""
  } ]
}

4.2.8 /_explain

该API可以针对一个指定的文档与一个指定的查询,得出文档之于查询的相关性得分。这种功能可以对“某个文档是否匹配某个查询”的问题给出有用的反馈。

使用该API时,需要给出指定的索引、类型以及文档ID。例如:

curl -XGET 'localhost:9200/twitter/tweet/1/_explain' -d '{
      "query" : {
        "term" : { "message" : "search" }
      }
}'

得出的响应内容大概如下:

{
  "matches" : true,
  "explanation" : {
    "value" : 0.15342641,
    "description" : "fieldWeight(message:search in 0), product of:",
    "details" : [ {
      "value" : 1.0,
      "description" : "tf(termFreq(message:search)=1)"
    }, {
      "value" : 0.30685282,
      "description" : "idf(docFreq=1, maxDocs=1)"
    }, {
      "value" : 0.5,
      "description" : "fieldNorm(field=message, doc=0)"
    } ]
  }
}

4.2.8 /_percolator

percolator词义本身是滤壶的意思。那这个“滤壶”在这里指的是什么呢?

通常来讲,我们都是使用“检索查询”来搜索“文档”,这背后的隐含意义其实是二者存在着一个匹配关系。那么,这种匹配关系是双向的,也就是说,当一个“检索查询”定义匹配一个“文档”时,也可以说是这个“文档”匹配了这个“检索查询”。

所以,ES现在提供了一种功能,让我们可以将“检索定义”存储在索引中,然后通过/_percolator就可以提交“文档”来试着找出其匹配的“检索查询”了。

那照这个意思来说,事先存储在索引中的查询检索定义就是所谓的“滤壶”了,用来过滤文档。

“查询检索定义”和“文档”二者位置可以互换的原因在于,它们都是JSON数据这个统一性基础。

/_percolatorAPI是一种实时的工作方式,一个percolator query被索引后,就可以马上应用于percorlator API中。只是有一点需要注意的是,在percorlator query中涉及的字段引用,必须是已经被相关索引定义过了的。

使用样例,首先注册一个percolator query:

curl -XPUT 'localhost:9200/my-index/.percolator/1' -d '{
    "query" : {
        "match" : {
            "message" : "bonsai tree"
        }
    }
}'

提交一个文档来试图匹配已经注册过的percolator query:

curl -XGET 'localhost:9200/my-index/my-type/_percolate' -d '{
    "doc" : {
        "message" : "A new bonsai tree in the office"
    }
}'

以上请求将会得到以下响应内容:

{
    "took" : 19,
    "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
    },
    "total" : 1,
    "matches" : [ 
        {
          "_index" : "my-index",
          "_id" : "1"
        }
    ]
}

前面说了percolator query会被存储在索引中,那么它自然也遵循了ES中关于索引存储的规范和约定。percolator query可以被存储在任意一个索引中的.percolator类型下。不过通常推荐的做法是,使用一个专用的索引来存储所有的percolator query。那么再回到percolate API的使用上来,使用时必须指定三个必须的参数:

  • index:包含.percolator类型的索引(存储了percolator query的索引)
  • type:被过滤文档的类型(在index中被定义过,显示地或者是被动地)
  • doc:被过滤文档
curl -XGET 'localhost:9200/twitter/tweet/_percolate' -d '{
        "doc" : {
                "created_at" : "2010-10-10T00:00:00",
                "message" : "some text"
        }
}'

percolator的内部机制是这样的:ES将会把.percolator中的查询定义解析为Lucene查询并且维护在内存中。当提交percolator文档时,请求中的文档会被解析为Lucene文档并存储到内存中的一个Lucene索引中(一次只能保存住一个文档),然后所有的percolator查询都会被作用于这个索引上,最终得出查询命中结果。

查看更多Percolator参考

5.Aggregation API

ES提供了强大的聚合计算功能,这使得用户在数据检索查询的基础之上能够聚合分组数据,并再基于分组进行聚合计算。聚合计算是针对一个数据集进行的,这个数据集被称作聚合计算上下文。那么最大的聚合计算上下文就是最外层的数据检索直接得到的数据检索结果了。

便于对聚合计算的理解,可以对聚合计算操作分为以下三大类别:

  • Bucketing
  • Metric
  • Pipeline

输入图片说明

Bukecting的含义就是“分桶”,很形象的说明了它更多地对应了“聚合计算”中的“聚合”。它允许用户定义一些分类标准,然后当操作被执行时,就会使用这些分类标准来评估上下文中的每个文档,当文档满足相关标准后,就会落入相关的“桶”中。最终,用户得到的是一组bucket,每个bucket都拥有一组相关文档集。

Metric的含义就是指标计算,它更多地对应了“聚合计算”中的“计算”。它的作用就在于针对一组文档集来得出指定计算指标。

Pipeline的含义是“管道”,它提供了“基于聚合计算结果”的“聚合计算”连接能力。这个功能目前ES(version 2.2)并未对外正式释放,尚在实验性阶段。

以上说明介绍了aggregation API的基本分类。不过,关于aggregation最强大之处在于允许多个aggregation之间的嵌套执行。意思是:bucket可以形成新的aggregation上下文,在这个上下文中可以继续应用aggregation。需要特殊说明的是,只有Bucketing类型的aggregation才可以往下继续嵌套,因为只有它才能继续向下提供bucket列表这种细分上下文,Metric类型只能作为子aggregation。(另外,注意“嵌套”的概念,关于这点请区别于pipeline连接)

就像前文介绍的数据检索/_search的用法一样,使用aggregation也是向某个API访问点提交请求报文的套路。具体提交的内容大概长成以下这个样子:

"aggregations" : {
    "<aggregation_name>" : {
        "<aggregation_type>" : {
            <aggregation_body>
        }
        [,"meta" : {  [<meta_data_body>] } ]?
        [,"aggregations" : { [<sub_aggregation>]+ } ]?
    }
    [,"<aggregation_name_2>" : { ... } ]*
}

可以看到,对于aggregation定义而言这是一个森林结构。使用aggregations关键词来引导一组aggregation定义,每个aggregation定义都有一个逻辑上的名称。这个逻辑名称可以用来在响应结果中识别对应的聚合计算结果。在定义aggregation的时候,需要指定aggregation type,作为aggregation定义中第一个key出现,这个是必须的。然后,每种type可能都需要不同的设定参数,这些参数呢就直接设定type为key的object里即可。如果需要有子聚合,那么在于type平级的位置直接定义下一层aggregations即可。

以上是关于aggregation的概要说明,其它更多、更详细的说明本文中不再细表。(另开专题讨论)

6.Indices API

Indices API属于针对索引的管理性质的API,其管理范围包括各个索引、索引设置、别名、映射、索引模板等等。从分类角度,具体有以下内容:

输入图片说明

以上是关于索引管理方面API的概要说明,其它更多、更详细的说明本文中不再细表。(另开专题讨论)

7.Cat API

JSON数据是一种非常适合机器解析的数据。但另外一方面,对于人的视觉而言,尤其是ssh终端上展现时,JSON格式数据显得非常啰嗦、冗余。这时候其实更需要传统的表式数据。那么catAPI就是为了满足这个需求而存在的。

Cat API的访问点就是/_cat,直接访问这个地址将会列出其支持的所有命令:

$ curl http://localhost:9200/_cat
=^.^=
/_cat/allocation
/_cat/shards
/_cat/shards/{index}
/_cat/master
/_cat/nodes
/_cat/indices
/_cat/indices/{index}
/_cat/segments
/_cat/segments/{index}
/_cat/count
/_cat/count/{index}
/_cat/recovery
/_cat/recovery/{index}
/_cat/health
/_cat/pending_tasks
/_cat/aliases
/_cat/aliases/{alias}
/_cat/thread_pool
/_cat/plugins
/_cat/fielddata
/_cat/fielddata/{fields}

每个命令都可以接受help参数,进而可以得到该命令头部信息的解释:

$ curl http://localhost:9200/_cat/plugins?help
id          |   | unique node id
name        | n | node name
component   | c | component
version     | v | component version
type        | t | type (j for JVM, s for Site)
url         | u | url for site plugins
description | d | plugin details

master命令为例,显示出master节点信息:

$ curl http://localhost:9200/_cat/master
LUVZ_XYGSX-XX1YylIoSOQ DESKTOP-BR0F8C4 10.0.0.4 Tundra

每个命令都可以接受一个v参数,以显示(加上了表头的)详细信息:

$ curl http://localhost:9200/_cat/master?v
id                     host            ip       node
LUVZ_XYGSX-XX1YylIoSOQ DESKTOP-BR0F8C4 10.0.0.4 Tundra

每个命令都可以接受一个h参数,以定制表列:

$ curl http://localhost:9200/_cat/master?h=id,ip
LUVZ_XYGSX-XX1YylIoSOQ 10.0.0.4

更多关于Cat API

8.Cluster API

Cluster API是一族以/_cluster/_nodes为访问点,工作于集群管理范畴的API。涉及到以下内容:

输入图片说明

以上是关于索引管理方面API的概要说明,其它更多、更详细的说明本文中不再细表。(另开专题讨论)

猜你喜欢

转载自blog.csdn.net/u010325193/article/details/84877961
今日推荐