Elasticsearch从入门到放弃:文档CRUD要牢记

在Elasticsearch中,文档(document)是全部可搜索数据的最小单位。它被序列化成JSON存储在Elasticsearch中。每一个文档都会有一个惟一ID,这个ID你能够本身指定或者交给Elasticsearch自动生成。 html

若是延续咱们以前不恰当的对比RDMS的话,我认为文档能够类比成关系型数据库中的表。node

元数据

前面咱们提到,每一个文档都有一个惟一ID来标识,获取文档时,“_id”字段记录的就是文档的惟一ID,它是元数据之一。固然,文档还有一些其余的元数据,下面咱们来一一介绍数据库

  • _index:文档所属的索引名
  • _type:文档所属的type
  • _id:文档的惟一ID

有了这三个,咱们就能够惟一肯定一个document了,固然,7.0版本之后咱们已经不须要_type了。接下来咱们再来看看其余的一些元数据json

  • _source:文档的原始JSON数据
  • _field_names:该字段用于索引文档中值不为null的字段名,主要用于exists请求查找指定字段是否为空
  • _ignore:这个字段用于索引和存储文档中每一个因为异常(开启了ignore_malformed)而被忽略的字段的名称
  • _meta:该字段用于存储一些自定义的元数据信息
  • _routing:用来指定数据落在哪一个分片上,默认值是Id
  • _version:文档的版本信息
  • _score:相关性打分

建立文档

建立文档有如下4种方法:api

  • PUT /<index>/_doc/<_id>
  • POST /<index>/_doc/
  • PUT /<index>/_create/<_id>
  • POST /<index>/_create/<_id>

这四种方法的区别是,若是不指定id,则Elasticsearch会自动生成一个id。若是使用_create的方法,则必须保证文档不存在,而使用_doc方法的话,既能够建立新的文档,也能够更新已存在的文档。安全

在建立文档时,还能够选择一些参数。bash

请求参数

  • if_seq_no:当文档的序列号是指定值时才更新
  • if_primary_term:当文档的primary term是指定值时才更新
  • op_type:若是设置为create则指定id的文档必须不存在,不然操做失败。有效值为index或create,默认为index
  • op_type:指定预处理的管道id
  • refresh:若是设置为true,则当即刷新受影响的分片。若是是wait_for,则会等到刷新分片后,这次操做才对搜索可见。若是是false,则不会刷新分片。默认值为false
  • routing:指定路由到的主分片
  • timeout:指定响应时间,默认是30秒
  • master_timeout:链接主节点的响应时长,默认是30秒
  • version:显式的指定版本号
  • version_type:指定版本号类型:internal、 external、external_gte、force
  • wait_for_active_shards:处理操做以前,必须保持活跃的分片副本数量,能够设置为all或者任意正整数。默认是1,即只须要主分片活跃。

响应包体

  • **_shards**:提供分片的信息
  • **_shards.total**:建立了文档的总分片数量
  • **_shards.successful**:成功建立文档分片的数量
  • **_shards.failed**:建立文档失败的分片数量
  • **_index**:文档所属索引
  • **_type**:文档所属type,目前只支持_doc
  • **_id**:文档的id
  • **_version**:文档的版本号
  • **_seq_no**:文档的序列号
  • **_primary_term**:文档的主要术语
  • result:索引的结果,created或者updated

咱们在建立文档时,若是指定的索引不存在,则ES会自动为咱们建立索引。这一操做是能够经过设置中的action.auto_create_index字段来控制的,默认是true。你能够修改这个字段,实现指定某些索引能够自动建立或者全部索引都不能自动建立的目的。并发

更新文档

了解了如何建立文档以后,咱们再来看看应该如何更新一个已经存在的文档。其实在建立文档时咱们就提到过,使用PUT /<index>/_doc/<id>的方法就能够更新一个已存在的文档。除此以外,咱们还有另外一种更新文档的方法:app

POST /<index>/_update/<_id>less

这两种更新有所不一样。_doc方法是先删除原有的文档,再建立新的。而_update方法则是增量更新,它的更新过程是先检索到文档,而后运行指定脚本,最后从新索引。

还有一个区别就是_update方法支持使用脚本更新,默认的语言是painless,你能够经过参数lang来进行设置。在请求参数方面,_update相较于_doc多了如下几个参数:

  • lang:指定脚本语言
  • retry_on_conflict:发生冲突时重试次数,默认是0
  • **_source**:设置为false,则不返回任何检索字段
  • **_source_excludes**:指定要从检索结果排除的source字段
  • **_source_includes**:指定要返回的检索source字段

下面的一个例子是用脚原本更新文档

curl -X POST "localhost:9200/test/_update/1?pretty" -H 'Content-Type: application/json' -d'
{
    "script" : {
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params" : {
            "count" : 4
        }
    }
}
'

Upsert

curl -X POST "localhost:9200/test/_update/1?pretty" -H 'Content-Type: application/json' -d'
{
    "script" : {
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params" : {
            "count" : 4
        }
    },
    "upsert" : {
        "counter" : 1
    }
}
'

当指定的文档不存在时,可使用upsert参数,建立一个新的文档,而当指定的文档存在时,该请求会执行script中的脚本。若是不想使用脚本,而只想新增/更新文档的话,可使用doc_as_upsert。

curl -X POST "localhost:9200/test/_update/1?pretty" -H 'Content-Type: application/json' -d'
{
    "doc" : {
        "name" : "new_name"
    },
    "doc_as_upsert" : true
}
'

update by query

这个API是用于批量更新检索出的文档的,具体能够经过一个例子来了解。

curl -X POST "localhost:9200/twitter/_update_by_query?pretty" -H 'Content-Type: application/json' -d'
{
  "script": {
    "source": "ctx._source.likes++",
    "lang": "painless"
  },
  "query": {
    "term": {
      "user": "kimchy"
    }
  }
}
'

获取文档

ES获取文档用的是GET API,请求的格式是:

GET /<index>/_doc/<_id>

它会返回文档的数据和一些元数据,若是你只想要文档的内容而不须要元数据时,可使用

GET /<index>/_source/<_id>

请求参数

获取文档的有几个请求参数以前已经提到过,这里再也不赘述,它们分别是:

  • refresh
  • routing
  • **_source**
  • **_source_excludes**
  • **_source_includes**
  • version
  • version_type

而还有一些以前没提到过的参数,咱们来具体看一下

  • preference:用来 指定执行请求的node或shard,若是设置为_local,则会优先在本地的分片执行
  • realtime:若是设置为true,则请求是实时的而不是近实时。默认是true
  • stored_fields:返回指定的字段中,store为true的字段

mget

mget是批量获取的方法之一,请求的格式有两种:

  • GET /_mget
  • GET /<index>/_mget

第一种是在请求体中写index。第二种是把index放到url中,不过这种方式可能会触发ES的安全检查。

mget的请求参数和get相同,只是须要在请求体中指定doc的相关检索条件

request

GET /_mget
{
    "docs" : [
        {
            "_index" : "jackey",
            "_id" : "1"
        },
        {
            "_index" : "jackey",
            "_id" : "2"
        }
    ]
}

response

{
  "docs" : [
    {
      "_index" : "jackey",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 5,
      "_seq_no" : 6,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "user" : "ja",
        "tool" : "ES",
        "message" : "qwer"
      }
    },
    {
      "_index" : "jackey",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 1,
      "_seq_no" : 2,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "user" : "zhe",
        "post_date" : "2019-11-15T14:12:12",
        "message" : "learning Elasticsearch"
      }
    }
  ]
}

删除文档

CURD操做只剩下最后一个D了,下面咱们就一块儿来看看ES中如何删除一个文档。

删除指定id使用的请求是

DELETE /<index>/_doc/<_id>

在并发量比较大的状况下,咱们在删除时一般会指定版本,以肯定删除的文档是咱们真正想要删除的文档。删除请求的参数咱们在以前也都介绍过,想要具体了解的同窗能够直接查看官方文档

delete by query

相似于update,delete也有一个delete by query的API。

POST /<index>/_delete_by_query

它也是要先按照条件来查询匹配的文档,而后删除这些文档。在执行查询以前,Elasticsearch会先为指定索引作一个快照,若是在执行删除过程当中,要索引起生改变,则会致使操做冲突,同时返回删除失败。

若是删除的文档比较多,也可使这个请求异步执行,只须要设置wait_for_completion=false便可。

这个API的refresh与delete API的refresh参数有所不一样,delete中的refresh参数是设置操做是否当即可见,即只刷新一个分片,而这个API中的refresh参数则是须要刷新受影响的全部分片。

Bulk API

最后,咱们再来介绍一种特殊的API,批量操做的API。它支持两种写法,能够将索引名写到url中,也能够写到请求体中。

  • POST /_bulk

  • POST /<index>/_bulk

在这个请求中,你能够任意使用以前的CRUD请求的组合。

curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/json' -d'
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
'

请求体中使用的语法是newline delimited JSON(NDJSON)。具体怎么用呢?其实咱们在上面的例子中已经有所展示了,对于index或create这样的请求,若是请求自己是有包体的,那么用换行符来表示下面的内容与子请求分隔,即为包体的开始。

例如上面例子中的index请求,它的包体就是{ "field1" : "value1" },因此它会在index请求的下一行出现。

对于批量执行操做来讲,单条操做失败并不会影响其余操做,而最终每条操做的结果也都会返回。

上面的例子执行完以后,咱们获得的结果应该是

{
   "took": 30,
   "errors": false,
   "items": [
      {
         "index": {
            "_index": "test",
            "_type": "_doc",
            "_id": "1",
            "_version": 1,
            "result": "created",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "status": 201,
            "_seq_no" : 0,
            "_primary_term": 1
         }
      },
      {
         "delete": {
            "_index": "test",
            "_type": "_doc",
            "_id": "2",
            "_version": 1,
            "result": "not_found",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "status": 404,
            "_seq_no" : 1,
            "_primary_term" : 2
         }
      },
      {
         "create": {
            "_index": "test",
            "_type": "_doc",
            "_id": "3",
            "_version": 1,
            "result": "created",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "status": 201,
            "_seq_no" : 2,
            "_primary_term" : 3
         }
      },
      {
         "update": {
            "_index": "test",
            "_type": "_doc",
            "_id": "1",
            "_version": 2,
            "result": "updated",
            "_shards": {
                "total": 2,
                "successful": 1,
                "failed": 0
            },
            "status": 200,
            "_seq_no" : 3,
            "_primary_term" : 4
         }
      }
   ]
}

批量操做的执行过程相比屡次单个操做而言,在性能上会有必定的提高。但同时也会有必定的风险,因此咱们在使用的时候要很是的谨慎。

总结

本文咱们先介绍了文档的基本概念和文档的元数据。接着又介绍了文档的CRUD操做和Bulk API。相信看完文章你对Elasticsearch的文档也会有必定的了解。那最后就请你启动你的Elasticsearch,而后亲自动手试一试这些操做,看看各类请求的参数究竟有什么做用。相信亲手实验过一遍以后你会对这些API有更深的印象。

相关文章
相关标签/搜索