ES相关操作
# RESTful规范
# 介绍
RESTful是资源表现层状态转化的英文缩写。
资源就是我们访问的URI。
表现层就是资源的表现方式,比如版本号就是同一种资源的不同表现形式,一般用Accept和Content-Type来表示。
状态转化就是如果客户端想要操作服务器,就必须通过某种手段,让服务器端发生"状态转化",而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
所以RESTful架构就是每一个URI代表一种资源,客户端和服务器之间传递这种资源的某种表现层,客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
客户端用到的手段,只能是以下HTTP协议中四个表示操作方式的动词。
GET
- 用来获取资源POST
- 用来新建资源,也可以用于更新资源,但由于POST是非幂等的,所以用来新建最规范。PUT
- 用来更新资源DELETE
- 用来删除资源
# 资源幂等
幂等是对于服务端资源来说的,资源幂等的意思是一次请求和多次请求,但是都不会对资源本身产生影响、或者说只产生一次影响,这就是资源幂等。
比如每次对一个数加1,这样资源状态就会变化,这就不是幂等。
而每次更新成某个指定的数,比如把某个数变成4,则无论多少次PUT值都会是4,这就是幂等。
常见请求方法幂等性
- GET、HEAD用于获取资源,无论请求多少次,结果都不会改变,所以他们是幂等的。
- POST用于新建资源,每次调用都会有新的资源产生,因此不满足幂等性。
- PUT用于更新数据,我们以相同的请求多次调用,也只会产生一次影响,所以满足幂等性。
- DELETE用于删除资源,调用一次和多次对资源产生影响是相同的,同个资源没法被删多次,所以也满足幂等性。
# 常见误区
URI包含动词
因为"资源"表示一种实体,所以URI中不应该有动词,而是应该使用名词代替,动词应该放在HTTP协议中。
例如:
# 其中show是动词,这个URI就是设计错误 /messages/show/1 # 正确的写法应该是用GET方法来表示show GET /messages/1
1
2
3
4
5
如果某些动作是四个HTTP动词表示不了的,我们可以把动作做成一种资源,然后在请求体中定义动作的具体细节。
例如:网上汇款,从账户1向账户2汇款500元
# 错误的写法 POST /accounts/1/transfer/500/to/2 # 正确的写法,把动词transfer改成名词transaction,然后把转账操作放到请求体 POST /transaction HTTP/1.1 from=1&to=2&amount=500.00
1
2
3
4
5
6
URI包含版本号
我们可以把不同的版本理解成同一种资源的不同表现形式,所以应该采用同一个URI,版本号可以在HTTP请求头信息的Accept字段中进行区分。
例如:
# 错误的写法 http://www.example.com/app/1.0/foo http://www.example.com/app/1.1/foo # 正确的写法 POST /app/foo HTTP/1.1 Accept: version=1.0 POST /app/foo HTTP/1.1 Accept: version=1.1
1
2
3
4
5
6
7
8
9
10
# ES基础操作
ES的操作需要通过HTTP请求调用ES的API来完成,ES的API端口号是9200,我们可以通过该端口对ES进行增删改查等操作。
# 创建索引
请求方式:
PUT
请求地址:
http://主机地址:9200/索引名
- 例如:
http://192.168.10.11:9200/nginx_log
- 例如:
请求体:
{ "settings": { "number_of_shards": 分片数, "number_of_replicas": 单个分片的副本数 } }
1
2
3
4
5
6- 默认是1分片1副本数。应当遵循1.5~3倍原则创建分片,例如:如果我们有2个节点,则推荐创建的分片数最多不超过6(2x3)个。
# 查询索引
- 请求方式:
GET
- 请求地址:
- 查询指定索引 -
http://主机地址:9200/索引名
- 查询所有索引 -
http://主机地址:9200/_cat/indices?v
- 查询指定索引 -
# 删除索引
- 请求方式:
GET
- 请求地址:
http://主机地址:9200/索引名
# 创建文档
请求方式:
POST/PUT
- 创建自定义ID索引时可以使用PUT,但是创建随机ID索引时不能使用PUT,因为该操作不是幂等性。
请求地址:
- 创建随机ID索引 -
http://主机地址:9200/索引名/_doc
- 创建自定义ID索引:
http://主机地址:9200/索引名/_doc/自定义ID
http://主机地址:9200/索引名/_create/自定义ID
- 创建随机ID索引 -
请求体:
# 需要将文档数据以JSON格式传入请求体: { "键名": "值", "键名": "值", ... ... } # 例如: { "title": "phone", "price": "$2499", "stock": 2394 }
1
2
3
4
5
6
7
8
9
10
11
12
13
# 查询文档
- 请求方式:
GET
- 请求地址:
- 主键查询:
http://主机地址:9200/索引名/_doc/文档ID
- 全部查询:
http://主机地址:9200/索引名/_search
- 主键查询:
# 全量更新文档
会用本次请求体的数据,覆盖文档中的所有数据。ES的更新操作是直接把之前的老数据标记为删除状态,然后再添加一条新的。
请求方式:
PUT
请求地址:
http://主机地址:9200/索引名/_doc/文档ID
请求体:
{ "键名": "值", "键名": "值", ... ... }
1
2
3
4
5
# 局部更新文档
只会更新本次请求体中涉及的文档数据。ES的更新操作是直接把之前的老数据标记为删除状态,然后再添加一条新的。
由于是局部修改所以可能会不幂等,因为即便我们局部更新的值正常修改并且一直不变,但该资源的其他信息也可能会被服务器自己修改,比如说update_time等等,所以它是不幂等的操作。
请求方式:
POST
请求地址:
http://主机地址:9200/索引名/_update/文档ID
请求体:
{ "键名": "值", "键名": "值", ... ... }
1
2
3
4
5
# 删除文档
- 请求方式:
DELETE
- 请求地址:
http://主机地址:9200/索引名/_doc/文档ID
# 配置索引映射关系
请求方式:
PUT
请求地址:
http://主机地址:9200/索引名/_mapping
请求体:
{ # 表示要配置索引的字段属性 "properties": { # 指定要配置的字段 "键名字段": { # 匹配方式: # text:表示允许分词匹配 # keyword:表示不允许分词查询,只能完整匹配 "type": "匹配方式", # 指定该字段是否能被索引: # true:表示可以被索引,也就是可以通过该字段查询 # false:表示不能被索引,也就是不能通过该字段查询 "index": 布尔值 } } } # 例如: { "properties": { "name": { "type": "keyword", "index": true } } }
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
# 文档条件查询
# 条件查询(query)
# 请求报文
请求方式:
POST
请求地址:
http://主机地址:9200/索引名/_search
请求体:
# 格式: { "query": { # 匹配方式:match分词匹配、match_phrase完全匹配 "匹配方式": { "键名": "值" ... ... } } } # 例如: { "query": { "match": { "title": "手机" } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 匹配方式
match - 分词匹配
ES会对要查询的值进行分词,然后将其作为不同的关键字分别进行全文检索。
例如:
# 请求体是: {"query": {"match": {"name": "大小"}}} # 会匹配包含以下信息的文档: {name: "大牛"} {name: "小黄"}
1
2
3
4
5
6
match_phrase - 短语匹配
ES不会对要查询的值进行分词,会直接将完整的值作为独立的关键字进行全文检索。
例如:
# 请求体是: {"query": {"match_phrase": {"name": "大小"}}} # 会匹配包含以下信息的文档: {name: "我是大小人"} # 不会匹配以下文档: {name: "大牛"} {name: "小黄"}
1
2
3
4
5
6
7
8
9
term - 完全匹配
ES不会对要查询的值进行分词,只会匹配出指定键值完全相同的文档。
例如:
# 请求体是: {"query": {"term": {"name": "小花"}}} # 会匹配包含以下信息的文档: {name: "小花"} # 不会匹配以下文档: {name: "小黄"} {name: "花花"} {name: "小花芳华"}
1
2
3
4
5
6
7
8
9
10
# 多条件查询(bool)
# 请求报文
请求方式:
POST
请求地址:
http://主机地址:9200/索引名/_search
请求体:
# 格式: { "query": { "bool": { "bool逻辑": [ {"匹配方式": {"键名": "值"}}, {"匹配方式": {"键名": "值"}}, ... ... ], # 会对筛选出的结果,再次过滤 "filter": { # 范围筛选 "range": { "键名": {"条件运算": 值} } } } } } # 例如: { "query": { "bool": { "must": [ {"term": {"name": "小花"}}, {"term": {"native": "湖南"}}, ], "filter": { "range": { "age": {"lte": 17} } } } } }
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
# Bool逻辑
- must:表示其中条件需要全部成立,才算符合条件筛选出来,类似于AND。
- should:表示其中条件只要有一项成立,就算符合条件筛选出来,类似于OR。
- must_not:表示其中条件都不成立,才算符合条件筛选出来,类似于NOT。
- filter:过滤条件。
- range:范围条件筛选。
- eq:等于。
- gt:大于。
- gte:大于等于。
- lt:小于。
- lte:小于等于。
# 分页查询
请求方式:
POST
请求地址:
http://主机地址:9200/索引名/_search
请求体:
# 格式: { "from": 起始数据位置, "size": 每页数据条数 } # 例如: { "query": { "match": { "title": "手机" } }, "from": 0, "size": 2 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16- 起始位置计算公式:(页码-1)*每页条数
# 限制查询结果字段
查询结果中只展示指定的字段。
请求方式:
POST
请求地址:
http://主机地址:9200/索引名/_search
请求体:
# 格式: { "_source": ["字段","字段"...] } # 例如: { "query": { "match": { "title": "手机" } }, "_source": ["title","datetime"] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查询结果排序
请求方式:
POST
请求地址:
http://主机地址:9200/索引名/_search
请求体:
# 格式: { "sort": { "用于排序的字段": { "order": "排序方式" } } } # 例如: { "sort": { "stock": { "order": "desc" } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17- 排序方式:desc(降序)、asc(升序)。
# 聚合操作(aggs)
# 请求报文
请求方式:
POST
请求地址:
http://主机地址:9200/索引名/_search
请求体:
# 格式: { # 使用聚合操作 "aggs": { "名称(展示字段名)": "聚合操作": { "field": "用于分组操作的字段" } }, # 表示不展示原始数据,只展示统计结果 "size": 0 } # 例如: { "aggs": { "age_min": "min": { "field": "age" } }, "size": 0 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 聚合操作
- avg:取指定字段所有值加起来的平均值
- sum:取指定字段所有值加起来的总值
- min:取指定字段所有值中的最小值
- max:取指定字段所有值中的最大值
- count:取拥有指定字段的文档的总数
- terms:按指定字段分组统计各值的数量