ElasticSearch索引、文档基本操作

程序媛和她的猫

共 11043字,需浏览 23分钟

 ·

2021-10-16 13:48

一、ElasticSearc 数据结构

ES 用于索引和搜索的基本单位是文档,可以将其认为是 MySQL 里的一行。文档以类型来分组,类型中包含若干文档,类似 MySQL 表中包含若干行。最终,一个或多个类型存在于同一索引中,索引类似于 MySQL 中的数据库。

注意上面这段话中出现的两个“索引”,在学习 ES 的过程中,有三种“索引”,分别是不同的含义:
1、索引(名词):ES 中存储数据的一个结构,类似于 MySQL 中的数据库。
2、索引(动词):保存一个文档到索引(名词)的过程,类似于 SQL 语句中的 INSERT 关键词,如果该文档已经存在,就相当于 UPDATE 关键字。
3、索引(倒排索引):ES 使用一个叫做“倒排索引”的数据结构,提高数据检索速度,类似于 MySQL 中 B+ 树结构的索引。

我们可以画一个简单的对比图来类比 ElasticSearch 和传统关系型数据库 MySQL,从而帮助我们更好地理解 ES 的这些概念,见下图1。

图1

索引、类型、文档、字段在 ES 中的结构如下图2所示。

图2

从以上描述,我们可以知道,“索引+类型+文档ID”这个组合就可以“唯一”确定 ES 中的某篇文档,下面我们详细讲述一下什么是文档、类型和索引。

1、索引(Index)

索引是文档的容器,是一类文档的集合,等同于 MySQL 中的数据库。

索引命名规范:索引受文件系统的限制。仅可能为小写字母,不能下划线开头。

举例说明:比如电商系统,有一个 customer_info 索引,存储商城所有客户的信息。

2、类型(Type)

类型用于区分同一个索引下不同的数据类型,等同于 MySQL 中的表。

类型命名规范:类型名称可以包括除了null的任何字符,不能以下划线开头。7.0版本之后不再支持类型,默认为_doc。

举例说明:customer_info 索引中有两个类型,分别是 common_customer_info 和 vip_customer_info,分别表示普通客户信息和vip客户信息。

我们需要注意一下 ES 关于类型(Type)的改革

5.X 版本中,一个索引下可以创建多个类型
6.X 版本中,一个索引下只能存在一个类型
7.X 版本中,一个 Index 中只有一个默认的 Type,这个 Type 的名字为_doc。即在 7.x 版本的 ES 中,库表合一,Index 既可以被认为对应 MySQL 的 Database,也可以认为对应 Table。也可以这样理解:ES 实例对应 MySQL 实例中的一个 Database,Index 对应 MySQL 中的 Table,Document 对应 MySQL 中表的一行记录。
8.x 版本中,彻底废除 Type

为什么不建议使用类型呢?或者说,为什么 ES 后来也逐渐废弃了类型呢
这是因为 ES 是基于 Lucene 实现的搜索引擎,Lucene 的全文检索功能之所以快,是因为“倒排索引”的存在。而这种“倒排索引”是基于索引(index)生成的,并非类型(type),多个类型(type)反而会减慢搜索的速度。

为了保持 Elasticsearch “一切为了搜索” 的宗旨,适当的做些改变(废除类型)也是无可厚非的,也是值得的。

3、文档(Document)

ES 中的单条记录称为文档,等同于 MySQL 中的行。

举例说明:common_customer_info 中每一行就是一个普通客户的信息,包括客户的 name、age、home、birthday、hobbies 等信息。

4、字段(field)

文档由字段组成,ElasticSearch 中的字段等同于 MySQL 中的列。

字段命名规范:字段不能使用空格,对象类型可以使用点号“.”,举例,info 字段值是一个对象 name,name 对象包括两个属性 firstname 和 lastname。
"info.name.firstname""zhou"
"info.name.lastname""xuejiao"

相当于:

"info": {
  "name": {
    "firstname""zhou",
    "lastname""xuejiao"
  }
}

举例说明:3 示例中的 name、age、home、birthday、hobbies 就是字段。

5、映射(mapping)

在 ES 中,对于字段的定义称为映射,比如 name 字段映射为字符串类型。

二、索引、文档基本操作

1、RESTful 架构

RESTful 架构有以下几个特点
a.服务端的每一个资源都有一个 URI(统一资源标识符)与之对应。
b.客户端通过 HTTP 协议与服务端进行通信。
c.客户端通过四个 HTTP 动词(GET、POST、PUT、DELETE),对服务器端资源进行操作,GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE用来删除资源。

2、ES 的 RESTful 风格操作

图3

3、索引、文档基本操作案例

操作 ES 的索引、文档有多种方式,我们可以使用 curl 工具,或者使用 Kibana/Head 插件,来操作 ES 中的索引、文档,这两种方式我们都来了解

curl 是一个利用 URL 语法在 Linux 命令行下工作的文件传输工具。

(1)索引操作
a.创建索引

1、使用 curl 命令创建索引。

// 请求
curl -XPUT 'http://192.168.56.10:9200/common_customer_info?pretty' -H 'Content-Type: application/json' -d '
{
  "settings":{
    "number_of_shards":5, // 指定索引分片数量
    "number_of_replicas" : 1, // 指定索引副本分片数量
    "refresh_interval":"30s" // 指定索引刷新时间
  }
}'


// 响应
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "common_customer_info"
}
图4

2、使用 Kibana 创建索引。

// 请求
PUT /common_customer_info
{
  "settings":{
    "number_of_shards":5,
    "number_of_replicas" : 1,
    "refresh_interval":"30s" 
  }


// 响应
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "common_customer_info"
}
图5
b.查看索引

b.1 查看指定索引
1、使用 curl 命令查看指定索引。

// 请求,查看 common_customer_info 这个索引的信息
// 注意:如果是 GET 请求,curl 命令可以省略 -XGET
curl 'http://192.168.56.10:9200/common_customer_info'

// 响应
{
  "common_customer_info" : {
    "aliases" : { }, // 别名
    "mappings" : { }, // 映射,即索引中有哪些字段,以及字段的类型
    "settings" : { // 索引配置
      "index" : {
        "refresh_interval" : "30s",// 索引刷新时间
        "number_of_shards" : "5",// 分片数
        "provided_name" : "common_customer_info",// 索引名称
        "creation_date" : "1634191993054",// 索引创建时间
        "number_of_replicas" : "1",// 副本分片数
        "uuid" : "rHtVcwjMSn-4vyvWv_Rr-A",// 索引id
        "version" : {// 索引版本号
          "created" : "7080099"
        }
      }
    }
  }
}
图6

2、使用 Kibana 查看指定索引。

// 请求
GET /common_customer_info

// 响应
{
  "common_customer_info" : {
    "aliases" : { },
    "mappings" : { },
    "settings" : {
      "index" : {
        "refresh_interval" : "30s",
        "number_of_shards" : "5",
        "provided_name" : "common_customer_info",
        "creation_date" : "1634191993054",
        "number_of_replicas" : "1",
        "uuid" : "rHtVcwjMSn-4vyvWv_Rr-A",
        "version" : {
          "created" : "7080099"
        }
      }
    }
  }
}
图7

b.2 查看所有索引
1、使用 curl 命令查看所有索引。

// 请求
curl http://192.168.56.10:9200/_cat/indices\?v

// 响应
health status index                          uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   .kibana-event-log-7.8.0-000001 LjLds26VQV2kafEdLV57Zg   1   0          5            0     25.7kb         25.7kb
green  open   .apm-custom-link               n_FOUb-jQaqQJXeYTzQ85A   1   0          0            0       208b           208b
green  open   .kibana_task_manager_1         HaE-DEJmT9WoC4RbT-oCCg   1   0          5            0     45.4kb         45.4kb
green  open   kibana_sample_data_ecommerce   K8HCVTKoSj-002bU6TxjBA   1   0       4675            0      4.2mb          4.2mb
green  open   .apm-agent-configuration       lBbpATyqRk-mQOVzSHK7Tw   1   0          0            0       208b           208b
yellow open   common_customer_info           rHtVcwjMSn-4vyvWv_Rr-A   5   1          0            0        1kb            1kb
green  open   .kibana_1                      DLYiBmlgR7G4CzOE8nNvIA   1   0        216            5      1.1mb          1.1mb
图8

2、使用 Kibana 查看所有索引。

// 请求
GET /_cat/indices\?v

// 响应和 1 一样
图8

b.3 根据条件查看索引
1、使用 curl 命令,根据条件查看索引。

// 请求,查看所有以字母“c”开头的索引
curl http://192.168.56.10:9200/_cat/indices/c\*\?v

// 响应
health status index                uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   common_customer_info rHtVcwjMSn-4vyvWv_Rr-A   5   1          0            0        1kb            1kb
图9
c.删除索引

1、使用 curl 命令删除索引。

// 请求,删除 common_customer_info 这个索引
curl -XDELETE 'http://192.168.56.10:9200/common_customer_info'

//响应
{
  "acknowledged":true
}
图10

2、使用 Kibana 删除索引。

// 请求
DELETE /common_customer_info

// 响应
{
  "acknowledged" : true
}
图11
(2)文档操作
a.创建文档(指定文档id)

1、使用 curl 命令,创建文档(指定文档id)。

// 请求,1 表示新建文档的 id。
curl -XPUT "http://192.168.56.10:9200/common_customer_info/_doc/1" -H 'Content-Type: application/json' -d'
{
    "name":"张三",
    "age":27,
    "home":"北京市朝阳区xx街道xx号",
    "birthday":"1996-05-11",
    "hobbies":[
        "跑步",
        "爬山",
        "撸猫"
    ]
}'


// 响应
{
  "_index" : "common_customer_info",// 文档所在的索引。
  "_type" : "_doc",// 文档所在的类型,我们使用的类型是 ES 7.x 版本默认的 _doc。
  "_id" : "1",// 文档id。
  "_version" : 1,// 文档的版本信息,每对文档更新一次,版本号就加1。ES 通过使用 version 来保证对文档的变更能以正确的顺序执行,,避免乱序造成的数据丢失。
  "result" : "created",// 执行结果,created 文档创建成功。
  "_shards" : {// 表示分片信息。
    "total" : 2,// 一共有2个分片,由下一句可以知道主分片数量为1,所以副本分片数量为 2-1=1 个。
    "successful" : 1,// 数据写入主分片,"successful" : 1 表示主分片有1个,并写入成功。
    "failed" : 0// 写入失败0个。
  },
  "_seq_no" : 0,// 严格递增的顺序号,是一个整数,保证后写入的文档的 _seq_no 大于先写入的文档的_seq_no。
  "_primary_term" : 1// 和 _seq_no 一样,也是一个整数,每当主分片发生重分配时,比如重启、主分片选举,_primary_term 会递增1。
}

ElasticSearch 使用 _version、_seq_no、_primary_term 这三个字段来做版本控制。

图12

2、使用 Kibana,创建文档(指定文档id)。

// 请求
PUT /common_customer_info/_doc/1
{
    "name":"张三",
    "age":27,
    "home":"北京市朝阳区xx街道xx号",
    "birthday":"1996-05-11",
    "hobbies":[
        "跑步",
        "爬山",
        "撸猫"
    ]
}

// 响应和 1 一样
图13
b.创建文档(不指定文档id,使用 ES 自动生成的文档id)

创建文档时,也可以不指定 id,此时 ES 会自动生成一个文档id,如果不指定 id,则需要使用 POST 请求,而不能使用 PUT 请求

1、使用 curl 命令,创建文档(不指定文档id,使用 ES 自动生成的文档id)。

// 请求
curl -XPOST "http://192.168.56.10:9200/common_customer_info/_doc" -H 'Content-Type: application/json' -d'
{
    "name":"张三",
    "age":27,
    "home":"北京市朝阳区xx街道xx号",
    "birthday":"1996-05-11",
    "hobbies":[
        "跑步",
        "爬山",
        "撸猫"
    ]
}'


// 响应
{
 "_index""common_customer_info",
 "_type""_doc",
 "_id""HyrDfXwBWK9BMtd1hplW",
 "_version": 1,
 "result""created",
 "_shards": {
  "total": 2,
  "successful": 1,
  "failed": 0
 },
 "_seq_no": 0,
 "_primary_term": 1
}
图14

2、使用 Kibana,创建文档(不指定文档id,使用 ES 自动生成的文档id)。

POST /common_customer_info/_doc
{
    "name":"张三",
    "age":27,
    "home":"北京市朝阳区xx街道xx号",
    "birthday":"1996-05-11",
    "hobbies":[
        "跑步",
        "爬山",
        "撸猫"
    ]
}

// 响应和 1 差不多,只有文档id不一样,因为 ES 生成的文档id是随机的。
图15
c.查询文档

c.1 查询当前索引当前类型中的所有文档
1、使用 curl 命令,查询当前索引当前类型中的所有文档。

// 请求
curl -XPOST http://192.168.56.10:9200/common_customer_info/_doc/_search

// 响应
{
  "took" : 7,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "common_customer_info",
        "_type" : "_doc",
        "_id" : "aCrGfXwBWK9BMtd1jJl3",
        "_score" : 1.0,
        "_source" : {
          "name" : "张三",
          "age" : 27,
          "home" : "北京市朝阳区xx街道xx号",
          "birthday" : "1996-05-11",
          "hobbies" : [
            "跑步",
            "爬山",
            "撸猫"
          ]
        }
      }
    ]
  }
}
图16

2、通过 Kibana,查询当前索引当前类型中的所有文档。

// 请求
POST /common_customer_info/_doc/_search

// 响应和 1 一样
图17

c.2 通过文档id查询文档
1、使用 curl 命令,通过文档 id 查询文档。

// 请求,查询 文档id=aCrGfXwBWK9BMtd1jJl3 的这条文档
curl http://192.168.56.10:9200/common_customer_info/_doc/aCrGfXwBWK9BMtd1jJl3

// 响应
{
  "_index" : "common_customer_info",
  "_type" : "_doc",
  "_id" : "aCrGfXwBWK9BMtd1jJl3",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "name" : "张三",
    "age" : 27,
    "home" : "北京市朝阳区xx街道xx号",
    "birthday" : "1996-05-11",
    "hobbies" : [
      "跑步",
      "爬山",
      "撸猫"
    ]
  }
}
图18

2、通过 Kibana,通过文档id查询文档。

// 请求
GET /common_customer_info/_doc/aCrGfXwBWK9BMtd1jJl3

// 响应和 1 一样
图19
d.更新文档

d.1 根据文档id修改部分字段
在修改字段时,有一个坑大家需要注意,我就经常掉坑里(尴尬)。

我们想要修改上面那个文档,将 name 字段由"张三"修改成"李四",我想当然地写出如下请求命令:

curl -XPOST "http://192.168.56.10:9200/common_customer_info/_doc/aCrGfXwBWK9BMtd1jJl3" -H 'Content-Type: application/json' -d'
{
    "name":"李四"
}'

执行这条命令,响应结果如下:

{
 "_index""common_customer_info",
 "_type""_doc",
 "_id""aCrGfXwBWK9BMtd1jJl3",
 "_version": 2,
 "result""updated",
 "_shards": {
  "total": 2,
  "successful": 1,
  "failed": 0
 },
 "_seq_no": 1,
 "_primary_term": 2
}

我们再去看一下这个文档,发现name字段值修改成功了,但是整个文档只剩下 name 一个字段了,其他字段全都没了。这是因为使用这种方式,更新的文档 {"name": "李四"} 会覆盖掉原来的文档。

如果不想让除了修改字段之外的其他字段丢失,请求体中必须指明 doc,将要修改的字段放到 doc 中

// curl 命令
curl -XPOST "http://192.168.56.10:9200/common_customer_info/_doc/aCrGfXwBWK9BMtd1jJl3" -H 'Content-Type: application/json' -d'
{  
  "doc":{
    "name":"李四"
  }
}'

// Kibana
POST /common_customer_info/_doc/aCrGfXwBWK9BMtd1jJl3/_update
{
  "doc":{
    "name":"李四"
  }
}

d.2 根据其他条件修改
我们想根据哪个字段(除了文档id)修改文档,就把这个字段放到 script 脚本中。

在 script 脚本中,lang 表示脚本语言,painless 是 es 内置的一种脚本语言。source 表示具体执行的脚本,ctx 是一个上下文对象,通过 ctx 可以访问到 _source、_title 等。

// 请求,将 name = 李四 的 home 修改为 吉林省长春市xx街道xx号
curl -XPOST "http://192.168.56.10:9200/common_customer_info/_doc/_update_by_query" -H 'Content-Type: application/json' -d'
{
  "query":{
    "match":{
      "name":"李四"
    }
  },
  "script":{
    "source":"ctx._source.home = \"吉林省长春市xx街道xx号\""
  }
}'

f.删除文档

f.1 根据文档id删除文档

// 删除文档 id=aCrGfXwBWK9BMtd1jJl3 这条文档
curl -XDELETE "http://192.168.56.10:9200/common_customer_info/_doc/aCrGfXwBWK9BMtd1jJl3"

f.2 根据其他字段(除了文档id)删除文档

// 删除 name=李四 的所有文档
curl -XPOST "http://192.168.56.10:9200/common_customer_info/_doc/_delete_by_query" -H 'Content-Type: application/json' -d'{
  "query":{
    "match":{
      "name":"李四"
    }
  }
}'


浏览 113
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报