声明
本文章是作者在阅读官方文档时的整理,因为作者水平有限,可能会有理解或翻译的不正确的地方,可以在下方留言
ElasticSearch APIs Introduction
用户和 ElasticSearch 交互的方式有两种,可以使用 ElasticSearch 的 Java Client,或者使用 RESTful API with JSON over HTTP。
Java API (Deprecated)
Java API,主要分为两种:
- 节点客户端(Node Client):节点客户端会作为一个非数据节点加入到集群中。
- 传输客户端(Transport Client):传输客户端是轻量级的,他本身不会作为节点加入到集群中,他会将请求转发到集群中的某个节点上。
这两种客户端都是通过9300端口,使用 ElasticSearch 的原生传输协议和集群交互。集群中的节点都是通过9300端口进行交互的,换言之,如果这个端口被关闭了,集群就无法正常交互了。
RESTful API with JSON over HTTP
所有编程语言则可以通过 RESTful API 通过端口9200和 ElasticSearch 通信,也是官方目前主推的方式,比如Java REST Client。你可以通过 Web 客户端发送 http 请求 ElasticSearch,甚至直接使用curl。一个 ElasticSearch 的请求格式
curl -X <VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -H 'Content-Type: application/json' -d '<BODY>'
# VERB : 适当的 HTTP 方法或谓词 GET(查询)、POST、PUT(添加或修改)、HEAD(验证文档是否存在) 或者 DELETE(删除)
# PROTOCOL : http 或者 https(如果你在 Elasticsearch 前面有一个 https 代理)
# HOST : ElasticSearch 节点的 host
# PORT : ElasticSearch 的 http 端口,默认是 9200
# PATH : API终端路径,如 _count 返回集群中命中文档的数量。可能由多个组件(路径)组成,如 _cluster/stats
# QUERY_STRING : 任意可选的查询字符串参数。如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读
# BODY : 一个 JSON 格式的请求体 (如果请求需要的话)
ElasticSearch 请求响应会返回一个 HTTP 状态码和一个 JSON 格式的返回值(HEAD 请求不会返回)。如果想在返回结果中看到 HTTP 头信息,可以使用curl的-i参数。 curl -i -XGET ...
$ curl -XPUT "http://47.95.116.74:9200/_settings" -u username:password -H 'Content-Type: application/json' -d'
{
"index": {
"max_result_window": "10000"
}
}'
Note: 如果使用 Kibana 的 Dev Tools,只需要
<VERB> <PATH>?<QUERY_STRING> <BODY>就可以了,Kibana 会自己添加其他的部分,写多了反而会出错!
接下来主要是介绍 RESTful API with JSON over HTTP 部分的 ElasticSearchAPIs
API Conventions
API Conventions,API 公约,就是 API 各部分参数在使用方式上的一些约定。ElasticSearch 允许通过 HTTP 公开 APIs,关于 HTTP 的设置目前不能动态改变,只能通过设置elasticsearch.yml生效。详细的配置参数
Multiple Indic
ElasticSearch 大多数引用index参数的 API 支持跨多个索引执行,例如同时对三个索引index1、index2、index3执行查询操作,GET /index1,index2,index3/_search?q=tag:wow,或者GET /_all/_search?q=tag:wow,其中_all代表所有的索引,除此之外还支持通配符*,如index*、in*ex*。如果只想查询index1和index2还可以使用-排除索引,如index*,-index3。所有支持Multiple IndicElasticSearch公约的 APIs 还支持下面的 URL 查询参数(?后面的查询参数):
ignore_unavailable:true或者false。是否需要忽略不存在或者被关闭的indexallow_no_indicElasticSearch:true或者false。如果通配符匹配不到具体的索引,请求是否失败。此设置适用于*、_all或者不指定索引,也适用于alias以防别名指向已经被关闭的索引。expand_wildcards: 可以指定为open、closed、open,closed、all、none。指定通配符要匹配哪些状态的索引。open是只匹配打开状态的索引,closed是只匹配关闭状态的索引,open,closed等同于all,匹配所有状态的索引,none表示关闭通配符。
URL 参数的使用取决于使用他们的 API 类型,单索引 APIs(Single-index APIs),例如
Document APIs和Single-index Alias APIs是不支持Multiple IndicElasticSearch特性的。
Date math support in index namElasticSearch
索引名称支持日期表达式
Document APIs
Document APIs 是对 ElasticSearch Document 的CRUD操作,即增查改删,主要是增删改,对于查询逻辑除了Get API之外,大部分是由Search APIs实现。Document APIs 按照数据的操作方式可以分为:
Single-document APIs
单条数据操作
Index API
对数据建立一条索引数据
Get API
根据文档的唯一_id查询一条文档,可以理解为主键查询。
Delete API
删除一条文档
Update API
更新一条文档
Multi-document APIs
多条数据操作
Multi Get API
Bulk API
Delete By Query API
Update By Query API
// 插入一条数
POST eps_data_test/_doc/1
{
"a":"a"
}
// 查看是否插入成功
GET eps_data_test/_search
// 将字段a="a"的记录的值,统一更新成"change_to_b"
POST eps_data_test/_update_by_query
{
"script": {
"source": "ctx._source['a'] = 'change_to_b'"
},
"query": {
"term": {
"a": "a"
}
}
}
// 查看是否更新成功,由于是异步操作,可能会有一定的延迟
GET eps_data_test/_search
Reindex API
- Reindex: 复制一个 Index 的 Documents 到另一个 Index
POST _reindex?slices=5&refresh
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter"
}
}
Search APIs
根据字段的值查询满足条件的文档,相对于 Get API,可以简单理解为条件查询。
Preparation before search
方便演示,首先添加几条数据,其中1是特定雇员的 ID,ElasticSearch 中的每个文档有默认属性_id作为唯一键,不写的话会默认生成一个uuid作为唯一键,这里设置_id = 1
PUT /megacorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interElasticSearchts": [ "sports", "music" ]
}
PUT /megacorp/employee/2
{
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interElasticSearchts": [ "music" ]
}
PUT /megacorp/employee/3
{
"first_name" : "Douglas",
"last_name" : "Fir",
"age" : 35,
"about": "I like to build cabinets",
"interElasticSearchts": [ "forElasticSearchtry" ]
}
先用 Get API 查找_id=1的雇员信息,做下验证
GET /megacorp/employee/1
Result:
{
"_index" : "megacorp", // 索引名称
"_type" : "employee", // 索引类型
"_id" : "1", // 文档ID
"_version" : 1, // 版本信息
"found" : true,
"_source" : { // 查找结果的原始 JSON 文档
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interElasticSearchts": [ "sports", "music" ]
}
}
Search APIs Introduction
Search APIs 使用关键字_search进行搜索,例如查询index=megacorp,type=employee的所有文档,具体语法如下:
GET /megacorp/employee/_search
Note: 高版本启用了了
type,直接忽略即可
Result:
{
"took": 6, // 花费时间,单位毫秒
"timed_out": false,
"_shards": { ... }, // 分片信息
"hits": { // 查询结果
"total": 3, // 总共命中 3 个文档
"max_score": 1, // 最大得分
"hits": [ // 所有命中的文档 JSON,默认只显示前 10 个
{
"_index": "megacorp",
"_type": "employee",
"_id": "3",
"_score": 1,
"_source": {
"first_name": "Douglas",
"last_name": "Fir",
"age": 35,
"about": "I like to build cabinets",
"interests": [ "forElasticSearchtry" ]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_score": 1,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "2",
"_score": 1,
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
对于 Search APIs 的条件搜索可以分为两类:
- Query-String: 更轻量的查询字符串,语法简洁(但这并不意味着易读)
- DSL(Domain Specific Language): 使用领域特定语言,使用一个 JSON 请求体作为参数进行搜索,适用于条件复杂的查询与分析,例如聚合计算
Query-String
搜索姓氏为 Smith 的雇员
GET /megacorp/employee/_search?q=last_name:Smith
Result:
{
...
"hits": {
"total": 2,
"max_score": 0.30685282,
"hits": [
{
...
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
...
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
使用?q=查询字符串参数,通过命令进行临时性的即时搜索非常方便,但是还是有局限性,比如查询参数会被 URL 编码(% 编码),可读性差、更加难懂,不利于调试,而且还有可能暴露隐私信息。因此不推荐直接向用户暴露此功能(有点像 Get 请求)。因此生产环境更推荐 RequestBody(Post 请求)。
DSL
使用 DSL,需指定使用一个 JSON 请求体作为参数,替代 Query-String,这种方式支持构建更复杂和健壮的查询,同样搜索姓氏为 Smith 的雇员。
GET /megacorp/employee/_search
{
"query": {
"match": { // match 查询,查询类型之一
"last_name" : "Smith"
}
}
}
ElasticSearch 的 Query DSL 是基于 JSON 的,可以将 Query DSL 看作查询的 AST(Abstract Syntax Tree),下面说一下 DSL 的使用规则,它由两类查询子句构成:
- Leaf query clauses:叶子查询子句,用于查找某特定字段为特定值的文档,例如查找
name=zhangqiang的文档,match、term或者range查询都属于叶子查询子句,并且他们可以单独使用。 - Compound query clauses:复合查询子句,复合查询子句包含叶子查询子句或者复合查询子句,他用逻辑操作(例如
bool或dis_max)将多个查询子句进行组合,或者修改他们的行为,如constant_score查询。
在query context和filter context使用查询子句的行为是不同的。
query context中使用的查询子句,返回的结果是这个文档与此查询子句有多么匹配,除了决定文档是否匹配外,它还会计算一个得分_score,表示文档和查询子句的匹配程度。filter context中使用的查询子句,返回的结果是这个文档和此查询子句是否匹配,是就是,不是就不是。使用过滤往往会被 elasticsearch 自动缓存起来以提高性能
炒个官方的栗子,来了解下query context和filter context
GET /secisland/_search
{
"query": { // 用关键字 query 指明它所对应的查询子句用于 query context
"bool": { // bool 和下面的两个 match 子句都属于 query context,用于说明文档有多么匹配
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
"filter": [ // 用关键字 filter 指明它所对应的查询子句用于 filter context,不影响 query 计算文档的 _score
{ "term": { "status": "published" }}, // term 和 range 子句属于 filter context
{ "range": { "publish_date": { "gte": "2015-01-01" }}} // 他们会将不匹配的文档过滤掉
]
}
}
}
Context 的使用原则:将影响文档匹配程度的查询子句放在
query context中,其他的查询子句放在filter context中。
按照查询字段的类型(text,keyword),以及字段的值是否被分词,可以将查询子句分为Full text queries和Term level queries。
Full text queries
首先了解下 Analysis 阶段,它会按照设定的分词器(Analyser),对类型为text的字段内容进行分词,分词器可以是 Elasticsearch 内置的,也可以是自定义的。分词发生在下面的两种情况下:
- Index Time Analysis: 在建立索引的时候对字段类型为
text的字段进行分词,每个text类型的字段都可以指定一个analyser,例如:// 为字段 title 配置单独的分词器 PUT my_index { "mappings": { "_doc": { "properties": { "title": { "type": "text", // 在索引阶段,text 类型的字段如果没有指定 analyser,它会在 Index Settings 中 // 查找名为 default 的 analyzer,如果没找到则默认使用 standard 分词器 "analyzer": "standard" } } } } } // 为 index 设置全局的分词器 PUT my_index { "settings": { "analysis": { "analyzer": { "default": { "type": "ik_max_word" } } } } } // 与上面的等价 PUT my_index { "settings": { "index.analysis.analyzer.default.type": "ik_max_word" } } - Search Time Analysis: 在搜索的时候对
query string进行分词。默认会按照下面的优先级来选择应该使用哪一个分词器- An
analyzerspecified in the full-text query itself.GET /my_index/_search { "query": { "match" : { "message": "身份证", "analyser": "ik_smart" } } } - The
search_analyzerdefined in the field mapping.PUT my_index { "settings": { "analysis": { "analyzer": { "my_ngram_analyzer": { "tokenizer": "my_ngram_tokenizer" } }, "tokenizer": { "my_ngram_tokenizer": { "type": "ngram", "min_gram": 3, "max_gram": 3, "token_chars": [ "letter", "digit" ] } } } } } PUT my_index { "mappings": { "_doc": { "properties": { "title": { "type": "text", "search_analyzer": "my_ngram_analyzer" } } } } } - The
analyzerdefined in the field mapping.PUT my_index { "settings": { "analysis": { "analyzer": { "my_ngram_analyzer": { "tokenizer": "my_ngram_tokenizer" } }, "tokenizer": { "my_ngram_tokenizer": { "type": "ngram", "min_gram": 3, "max_gram": 3, "token_chars": [ "letter", "digit" ] } } } } } PUT my_index { "mappings": { "_doc": { "properties": { "title": { "type": "text", "analyzer": "my_ngram_analyzer" } } } } } - An analyzer named
default_searchin the index settings.PUT my_index { "settings": { "analysis": { "analyzer": { "default_search": { "type": "ik_max_word" } } } } } // 或者下面的写法 PUT my_index { "settings": { "index.analysis.analyzer.default_search.type": "ik_max_word" } } - An analyzer named
defaultin the index settings.PUT my_index { "settings": { "analysis": { "analyzer": { "default": { "type": "ik_max_word" } } } } } // 或者下面的写法 PUT my_index { "settings": { "index.analysis.analyzer.default.type": "ik_max_word" } } - The
standardanalyzer.
- An
match
match query可以查询text/numric/dates类型的字段,match实质上是bool查询,也就是说它是对分词后的文本构造bool query,operator可以设置为or(default)或and,来控制匹配结果。下面举个例子
// 添加一条数据
PUT /my_index/_doc/1
{
"title": "brown fox"
}
// operator 默认是 or,
// `blue fox`会被 analyser 分词成为`blue`和`fox`,
// `brown fox`会被 analyser 分词为`brown`和`fox`
// 其中`fox`可以匹配上,因此是可以匹配到`brown fox`的
GET my_index/_search
{
"query": {
"match" : {
"title": {
"query": "blue fox"
// operator 默认为 or
}
}
}
}
// 上面的查询和下面的查询等价
GET /my_index/_search
{
"query": {
"bool": {
"should": [
{ "term": { "title": "brown" }},
{ "term": { "title": "fox" }}
]
}
}
}
// 下面把`operator`变成`and`,因为`blue`没有正确的匹配到结果,
// `false and true => false`因此这个查询匹配不到`brown fox`
GET /my_index/_search
{
"query": {
"match" : {
"title": {
"query": "blue fox",
"operator": "and"
}
}
}
}
// 上面的查询和下面的查询等价
GET /my_index/_search
{
"query": {
"bool": {
"must": [
{ "term": { "title": "brown" }},
{ "term": { "title": "fox" }}
]
}
}
}
可以使用minimum_should_match控制至少要匹配的should子句数,的详细介绍可以参考minimum_should_match
// 如果加上 minimum_should_match,它会直接传入到 bool 查询中,详细的介绍可以参考
GET /my_index/_search
{
"query": {
"match": {
"title": {
"query": "quick brown fox",
// 3 * 75% 向下取整 = 2
"minimum_should_match": "75%"
}
}
}
}
// 上面的查询和下面的查询等价
GET /my_index/_search
{
"query": {
"bool": {
"should": [
{ "term": { "title": "brown" }},
{ "term": { "title": "fox" }},
{ "term": { "title": "quick" }}
],
"minimum_should_match": 2
}
}
}
Note: 如果
index time analysis和search time analysis指定了不同分词器,有可能会匹配不到,比如对于身份证:index time analysis指定为standard,会分词为身、份、证,而search time analysis指定为ik_smart会分词为身份证,这样就没有能正确匹配上的term项,从而导致没有查询结果。
Term level queries
组合查询:bool
minimum_should_match指明最少满足几个should条件才匹配,如果一个bool操作中只有should操作,那么minimum_should_match默认为1,如果除了should还有must、filter等其他操作,minimum_should_match默认为0
GET hsyk_diseases_info/_search
{
"query": {
"bool":{
"must": [
{ "term":{ "PROVINCE.keyword": "辽宁省" } }
],
"should": [
{ "term":{ "CITY.keyword": "大连市" } },
{ "term":{ "CITY.keyword": "沈阳市" } }
],
"filter": [
],
"minimum_should_match" : 1
}
},
"aggs" : {
"group_by_district" : {
"terms" : {
"field":"DISTRICT.keyword"
}
}
}
}
组合查询:bool嵌套
GET hsyk_diseases_info/_search
{
"query": {
"bool": {
"filter": [
{
"bool": {
"should": [
{ "term":
{"PROVINCE.keyword": "辽宁省"}
}
]
}
},
{
"bool": {
"should": [
{ "terms":
{ "CITY.keyword": ["沈阳市", "大连市"]}
}
]
}
}
]
}
},
"aggs": {
"group_by_district": {
"terms": {
"field": "DISTRICT.keyword"
}
}
}
}
Aggregations
Indices APIs
cat APIs
cat APIs,主要用于集群维护,用来查看集群相关的一些信息,例如节点信息,索引状态等
# 查看主节点信息
GET /_cat/master
u_n93zwxThWHi1PDBJAGAg master 127.0.0.1 node-1
# 通用参数 `v` verbose output,显示 header
GET /_cat/master?v
id host ip node
u_n93zwxThWHi1PDBJAGAg master 127.0.0.1 node-1
# 通用参数 `help`,显示命令包含的所有列的说明
GET /_cat/master?help
id | | node id
host | h | host name
ip | | ip address
node | n | node name
# 通用参数 `h`,指定要查看的列
GET /_cat/nodes?h=ip,port,heapPercent,name
127.0.0.1 9300 27 node-1
# 也可使用通配符,如
GET /_cat/thread_pool?h=ip,queue*
# numberic formats
# 对于一些提供数字类型输出的命令,如 bytes、size、time,默认都是方便人阅读的,如`3763212`
# 会转换成`3.5mb`,这样就无法进行排序了,如果想排序就需要转换成 numberic 类型的值
$curl '192.168.56.10:9200/_cat/indices?bytes=b' | sort -rnk8
green wiki2 3 0 10000 0 105274918 105274918
green wiki1 3 0 10000 413 103776272 103776272
green foo 1 0 227 0 2065131 2065131
# repsonse as text(default),json,smile,yaml,cbor
# `pretty` 结果格式化
$curl 'localhost:9200/_cat/indices?format=json&pretty'
[
{
"pri.store.size": "650b",
"health": "yellow",
"status": "open",
"index": "twitter",
"pri": "5",
"rep": "1",
"docs.count": "0",
"docs.deleted": "0",
"store.size": "650b"
}
]
Cluster APIs
Common Operations
定期删除过期的 Indices
_ttl属性在5.0+弃用了,7.0+出现ilm的概念,即Index lifecycle management
- define policy,超过 30 天删除。
PUT _ilm/policy/logs_expire_policy { "policy": { "phases": { "delete": { "min_age": "30d", "actions": { "delete": {} } } } } } - create index template,指定
index.lifecycle.name// 对于 index name 为 logs-* 的索引,使用 logs_expire_policy ilm,优先级为 10 PUT _template/logs_date_detection { "order" : 10, "index_patterns" : ["logs-*"], "settings": { "index.lifecycle.name": "logs_expire_policy" }, "mappings": { "date_detection" : true } }
Note:指定
min_age=30d并不是恰巧在过了30d整的时候删除,而是需要等到 ILM 去检查的时候发现已经过了30d才会删除,如果对时间要求严格,还是自己写crontab吧
复制索引
# clone 索引,index `source_index` must be read-only to resize index. use `index.blocks.write=true`
POST /source_index/_clone/dest_index
# 重建索引
POST /_reindex
{
"source": {
"index": "source_index"
},
"dest": {
"index": "dest_index"
}
}


