前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习ElasticSearch的Restful Api快速掌握ES数据的增删改查

学习ElasticSearch的Restful Api快速掌握ES数据的增删改查

作者头像
用户3587585
发布2024-06-13 19:05:12
600
发布2024-06-13 19:05:12
举报
文章被收录于专栏:阿福谈Web编程阿福谈Web编程
在开始进行ES的读写操作之前,我们来捋一捋ES的写请求原理

1 ElasticSearch的请请求原理

ES服务器写单个文档的流程图如下(图片来自官网)

以下是写单个文档所需要的步骤:

1) 客户端向NODE1发送写请求

2) 检查active的shard数

3)NODE-1使用文档ID来确定文档属于分片0,通过集群状态中的内容路由表信息获知分片0的主分片位于NODE3,因此请求被转发到NODE3上

4)NODE3上的主分片执行写操作。如果写入成功,则它将请求并行转发到NODE1和NODE3的副分片上,等待返回结果。当所有的副分片都报告成功, NODE3 将向协调节点报告成功,协调节点再向客户端报告成功 。

在客户端收到成功响应时,意味着写操作已经在主分片和所有副分片都执行完成。

1.1 为什么要检查active的shard数?

ES中有个参数叫write.wait_for_active_shards,这个参数是Index的一个setting,也可以再请求中带上这个参数,

这个参数的含义是每次在写入前该shard至少具有的active副本数。假设我们有一个Index,每个shard有3个Replica,加上Primary总共有4个副本。如果配置write.wait_for_active_shards的数为3,那么允许最多有一个Replica挂掉。如果有两个Replica挂掉,则active的副本数不足3,此时不允许写入。

这个参数默认是1,即只要Primary在就可以写入,起不到什么作用。如果配置大于1,可以起到一种保护作用,保证写入的数据具有更高的可靠性。但是这个参数只在写入前进行检查,并不保证这些数据一定在这些这些副本上写入成功,所以并不是严格保证了写入了多少个副本。

在以前的版本中是写一致性机制,现在被替换为write.waif_for_active_shards参数

  • one:要求我们这个写操作,只要有一个primary shard是active活跃可用的,就可以执行
  • all:要求我们这个写操作,必须所有的primary shard和replica shard都是活跃的,才可以执行这个写操作
  • quorum:要求所有的shard中,必须是大部分的shard都是活跃的,可用的,才可以执行这个写操作

写一致性的默认策略是 quorum,即多数的分片(其中分片副本可以是主分片或副分片)在写入操作时处于可用状态。

代码语言:javascript
复制


put /index/type/id?consistency=quorum
quroum = int( (primary + number_of_replicas) / 2 ) + 1

一些参数解释:

参数

简介

version

设置文档版本号,主要用于实现乐观锁

version_type

版本类型

opt_type

可设置为create,仅代表在文档不存在时才写入,如果文档已存在,则写请求失败

routing

ES默认使用文档ID进行路由,指定routing可使用routing值进行路由

wait_for_active_shards

用于控制写一致性,当指定数量的分片副本可用时才执行写入,否则重试直至超时。默认值为1,主分片可用时即执行写入

refresh

写入完毕后执行刷新,使搜索可见

timeout

请求超时时间,默认为1分钟

pipeline

指定事先创建好的pipeline名称

1.2 写入Primary完成之后,为何要等待所有的Replica响应(或连接失败)后返回?
在更早的ES版本,Primary和Replica之间使允许异步复制的,即写入Primary成功即可返回。但这种模式下,如果Primary挂掉,就有丢失数据的风险,而且从Replica读数据也很难保证读到最新的数据。所以后来ES就取消了异步模式了,改成了等Primary等Replica返回之后再返回给客户端。

因为Primary要等Replica返回后再返回给客户端,那么延迟就会收到最慢的Replicate的影响,这确实是目前ES架构的一个弊端。之前曾误认为这里是等wait_for_active_shards个副本写入成功即可返回,但是后来读源码发现是等所有Replica返回的。

如果Replica写入失败,ES会执行一些重试逻辑等,但最终并不强求一定要在多少个节点写入成功。在返回的结果中,会包含数据在多少个shard中写入成功了,多少个失败了。

2 Restful API

说明:以下所有操作均在本地启动ES服务器和Kibana服务器后通过登录http://localhost:5601网页的Console控制台中进行

2.1 创建空索引
代码语言:javascript
复制
PUT /heshengfu
{
    "settings": {
        "number_of_shards": 2,
        "number_of_replicas": 0,
        "write.wait_for_active_shards": 1
    }
}

2.2 修改副本
代码语言:javascript
复制
PUT /heshengfu/_settings
{
    "number_of_replicas": 2
}

2.1 创建索引student并插入数据
代码语言:javascript
复制
POST /student/_doc/1001 
{ 
  "id":1001, 
  "name":"张三", 
  "age":20,
   "sex":"男" 
} 
//插入成功后的响应信息 
{ 
  "_index" : "student", 
  "_type" : "_doc",
   "_id" : "1001", 
   "_version" : 1, 
   "result" : "created", 
   "_shards" : 
     { "total" : 2, 
       "successful" : 1, 
       "failed" : 0 
       }, 
       "_seq_no" : 0, 
       "_primary_term" : 1
     }
 //不指定id,ES帮我们自动生成
POST /student/_doc
{
  "id": 1002,
  "name": "李四",
  "age":21,
  "sex": "男"
}
//插入成功后响应信息
{
  "_index" : "student",
  "_type" : "_doc",
  "_id" : "I5KOOXQBvGaqBPqBTsaI",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}
//可以看到ES给这个文档分配的Id为"I5KOOXQBvGaqBPqBTsaI",而请求json体中的id参数是失效的
//再添加一条数据
POST /student/_doc
{
  "name": "晓雪",
  "age": 20,
  "sex": "女"
}
//返回信息
{
  "_index" : "student",
  "_type" : "_doc",
  "_id" : "JpLOOnQBvGaqBPqBU8aa",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 8,
  "_primary_term" : 1
}  
代码语言:javascript
复制
2.3 更新数据

在Elasticsearch中,是不允许直接修改文档数据的,但是可以通过覆盖的方式进行更新

代码语言:javascript
复制
//通过下面这种方式来更新更新
POST /student/_update/JpLOOnQBvGaqBPqBU8aa

  "doc":{
     "age": 22
  }
}

替换和更新的不同:替换是每次都会去替换,更新是有新的东西就更新,没有新的修改就不更新,更新比替换的性能好

3 删除操作

代码语言:javascript
复制
3.1 删除索引

DELETE /stuent
//慎用,业务中需要时才执行此操作
3.2 删除数据
代码语言:javascript
复制
//先插入一条数据,然后再执行删除
POST /student/_doc
{
  "name": "王五",
  "age": 25,
  "sex": "男"
}
//返回文档的id为JZKsOXQBvGaqBPqBj8b9
//执行删除操作
DELETE /student/_doc/JZKsOXQBvGaqBPqBj8b9
//返回信息如下
{
  "_index" : "student",
  "_type" : "_doc",
  "_id" : "JZKsOXQBvGaqBPqBj8b9",
  "_version" : 2,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 5,
  "_primary_term" : 1
}
4 查询数据
4.1 根据ID搜索数据
代码语言:javascript
复制
GET /student/_doc/1001
//响应信息
{
  "_index" : "student",
  "_type" : "_doc",
  "_id" : "1001",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "id" : 1001,
    "name" : "张三",
    "age" : 20,
    "sex" : "男"
  }
}
4.2 查询全部数据
代码语言:javascript
复制
GET /student/_search  //默认最多返回10条数据
//响应信息如下:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "1001",
        "_score" : 1.0,
        "_source" : {
          "id" : 1001,
          "name" : "张三",
          "age" : 20,
          "sex" : "男"
        }
      },
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "I5KOOXQBvGaqBPqBTsaI",
        "_score" : 1.0,
        "_source" : {
          "id" : 1002,
          "name" : "李四",
          "age" : 21,
          "sex" : "男"
        }
      },
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "JpLOOnQBvGaqBPqBU8aa",
        "_score" : 1.0,
        "_source" : {
          "name" : "晓雪",
          "age" : 22,
          "sex" : "女"
        }
      }
    ]
  }
}

代码语言:javascript
复制
took:    ElasticSearch运行查询耗时(单位ms) 
timed_out: 搜索请求是否超时 
_shards: 搜索了多少碎片,并对多少碎片成功、失败或跳过进行了细分 
max_score: 找到最相关文档的得分 
hits.total.value: 找到了多少匹配的文档 
hits.sort: 文档的排序位置 
hits._sort: 文档的相关性评分(在使用match_all时不适用)
代码语言:javascript
复制

4.4 DSL查询

Elasticsearch提供丰富且灵活的查询领域特点语言查询叫做DSL查询(Query DSL),它允许你构建
更加复杂、强大的查询

//搜索年龄为21岁的学生
POST /student/_search
{
  "query":{
    "match":{
      "age": 21
    }
  }
}
//条件匹配,match中的字段为需要匹配的属性,
//其中的属性可以是student索引中document中的任何属性

对查询结果进行排序

代码语言:javascript
复制
POST /student/_search
{
  "query":{
    "match_all": {}
  },
  "sort": {
      "age": {
        "order": "desc"
      }
    }
}
//查询结果
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "JpLOOnQBvGaqBPqBU8aa",
        "_score" : null,
        "_source" : {
          "name" : "晓雪",
          "age" : 22,
          "sex" : "女"
        },
        "sort" : [
          22
        ]
      },
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "I5KOOXQBvGaqBPqBTsaI",
        "_score" : null,
        "_source" : {
          "id" : 1002,
          "name" : "李四",
          "age" : 21,
          "sex" : "男"
        },
        "sort" : [
          21
        ]
      },
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "1001",
        "_score" : null,
        "_source" : {
          "id" : 1001,
          "name" : "张三",
          "age" : 20,
          "sex" : "男"
        },
        "sort" : [
          20
        ]
      }
    ]
  }
}

对范围进行过滤

给student索引中每一个文档都加上enter_date字段

代码语言:javascript
复制
POST /student/_update/1001
{
  "doc": {
     "enter_date": "2018-10-01"
   }
}

POST /student/_update/I5KOOXQBvGaqBPqBTsaI
{
  "doc": {
     "enter_date": "2019-10-01"
   }
}

POST /student/_update/JpLOOnQBvGaqBPqBU8aa
{
  "doc": {
    "enter_date": "2017-10-01"
 }
}

对学生的入校日期进行过滤,且必须性别为男
GET /student/_search
{
  "query":{
    "bool":{
      "filter":{
        "range": {
          "enter_date": {
             "gt": "2019-06-01"
          }
        }
      },
      "must":{
        "match": {
          "sex": "男"
        }
      }
    }
  }
}

//查询结果如下:
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.47000363,
    "hits" : [
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "I5KOOXQBvGaqBPqBTsaI",
        "_score" : 0.47000363,
        "_source" : {
          "id" : 1002,
          "name" : "李四",
          "age" : 21,
          "sex" : "男",
          "enter_date" : "2019-10-01"
        }
      }
    ]
  }
}

  • gt :: 大于
  • gte :: 大于等于
  • lt :: 小于
  • lte :: 小于等于

短语查询

给每个学生添加一个address字段

代码语言:javascript
复制
POST /student/_update/1001
{
  "doc": {
    "address": "湖南长沙"
  }
}

POST /student/_updateI/5KOOXQBvGaqBPqBTsaI
{
  "doc": {
    "address": "湖南长沙"
  }
}

POST /student/_update/JpLOOnQBvGaqBPqBU8aa
{
  "doc": {
    "address": "广东广州"
  }
}
// 然后进行短语匹配查询
 GET /student/_search
  { 
    "query": 
      { 
        "match_phrase": { "address": "湖南长沙" } 
       } 
   } 
   //查询结果如下:
{
  "took" : 63,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 3.9233167,
    "hits" : [
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "I5KOOXQBvGaqBPqBTsaI",
        "_score" : 3.9233167,
        "_source" : {
          "id" : 1002,
          "name" : "李四",
          "age" : 21,
          "sex" : "男",
          "birth_day" : "1999-06-01",
          "enter_date" : "2019-10-01",
          "address" : "湖南长沙"
        }
      }
    ]
  }
}

注意:

  • match 中如果加空格,那么会被认为两个单词,包含任意一个单词将被查询到
  • match_parase 将忽略空格,将该字符认为一个整体,会在索引中匹配包含这个整体的文档

高亮显示

代码语言:javascript
复制
GET /student/_search
{
  "query": {
    "match": {
      "name": "晓雪"
    }
  },
  
  "highlight":{
      "fields": {
        "name":{}
      }
    }
}
//搜索结果如下:
{
  "took" : 75,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.9616584,
    "hits" : [
      {
        "_index" : "student",
        "_type" : "_doc",
        "_id" : "JpLOOnQBvGaqBPqBU8aa",
        "_score" : 1.9616584,
        "_source" : {
          "name" : "晓雪",
          "age" : 22,
          "sex" : "女",
          "birth_day" : "1998-06-01",
          "enter_date" : "2017-10-01",
          "address" : "广东广州"
        },
        "highlight" : {
          "name" : [
            "<em>晓</em><em>雪</em>"
          ]
        }
      }
    ]
  }
}

搜索时使用highlights.fields.field对需要的字段进行高亮显示

5 小结

本文对使用ElesticSearch的Restful API进行简单的增删改查进行了详细地示例演示,并涉及简单的根据ID搜索、全量搜素和稍微复杂的DSL搜索,希望读者看完本文都能有切实的收获。下一篇文章,笔者将介绍使用Restful API对数据进行批量和聚合操作,敬请期待

参考阅读

鲁班学院分布式系统ElesticSearch学习笔记文档

https://www.yuque.com/books/share/9f4576fb-9aa9-4965-abf3-b3a36433faa6/ice1ww

--END--

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 阿福谈Web编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1 为什么要检查active的shard数?
  • 1.2 写入Primary完成之后,为何要等待所有的Replica响应(或连接失败)后返回?
  • 在更早的ES版本,Primary和Replica之间使允许异步复制的,即写入Primary成功即可返回。但这种模式下,如果Primary挂掉,就有丢失数据的风险,而且从Replica读数据也很难保证读到最新的数据。所以后来ES就取消了异步模式了,改成了等Primary等Replica返回之后再返回给客户端。
  • 2.1 创建空索引
  • 2.2 修改副本
  • 2.1 创建索引student并插入数据
  • 2.3 更新数据
    • 3.2 删除数据
      • 4 查询数据
        • 4.1 根据ID搜索数据
          • 4.2 查询全部数据
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档