Elasticsearch文档的基本操作

文档索引

为了创建一个文档,es提供了好几种url:

POST http://<server>/<index_name>/<type>
PUT/POST http://<server>/<index_name>/<type> /<id>
PUT/POST http://<server>/<index_name>/<type> /<id>/_create

比如:

curl -XPOST
'http://localhost:9200/myindex/order/2qLrAfPVQvCRMe7Ku8r0Tw' -d '
{
    "id": "1234",
    "date": "2013-06-07T12:14:54",
    "customer_id": "customer1",
    "sent": true,
    "in_stock_items": 0,
    "items": [
        {
            "name": "item1",
            "quantity": 3,
            "vat": 20
        },
        {
            "name": "item2",
            "quantity": 2,
            "vat": 20
        },
        {
            "name": "item3",
            "quantity": 1,
            "vat": 10
        }
    ]
}'

如果文档索引成功的话,es会返回:

{
    "_index": "myindex",
    "_type": "order",
    "_id": "2qLrAfPVQvCRMe7Ku8r0Tw",
    "_version": 1,
    "forced_refresh": false,
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "created": true
}

在上面的返回中,有几个比较重要的信息:
_id 文档的唯一标识
version 文档的版本信息,用于乐观锁
created 标记文档是否创建了
在Elasticsearch中,索引一个文档主要有下面的几个步骤:

  • 根据文档的id或者routing/parent的原信息路由到正确的分片上,如果client端并没有提供文档id,那么Elasticsearch会自动生成一个文档id
  • 验证文档的json结构是否是正确的json格式
  • 根据mapping处理json文档,如果在被索引的文档中存在新在字段,并且mapping是可以更新的话,那么这个新的字段就会增加到mapping中。
  • 然后es会在路由到的分片上检索文档,如果文档id已经存在的话,那么就会更新文档
  • 如果被索引的文档包含嵌套的文档的话,那么es就会把这些嵌套的文档单独拿出来然后单独处理
  • 最后返回一些关于保存的这个文档的一些信息,比如id和版本号

一般情况下,建议id使用相同的长度,这样可以提高性能,因为可以改善存储它们的数据树的平衡。
除过上面的例子之外,Elasticsearch也允许传递索引API URL几个查询参数来控制文档的索引。 最常用的如下:

  • routing 来控制哪一个分片被用来索引文档
    curl -XPOST 'http://localhost:9200/myindex/order?routing=1'
  • parent 定义子文档的父项,并使用此值来指定路由。 并且必须在映射中指定父对象:
    curl -XPOST 'http://localhost:9200/myindex/order?parent=12'
  • timestamp 用于索引文档的时间戳。 必须在映射中激活它
    curl -XPOST 'http://localhost:9200/myindex/order?timestamp= 2013-01-25T19%3A22%3A22'
  • consistency (one/quorum/all) 默认情况下值为quorum,也就是(>replica/2+1),但是这个选项的值也可以修改:
    curl -XPOST 'http://localhost:9200/myindex/order? consistency=one'
  • replication (sync/async) 默认是sync,如果设置复制异步,那么我们仅在主分片上咨询索引操作,并在次分片上异步执行。 以这种方式运行的话,API调用会更快地返回响应动作:
    curl -XPOST 'http://localhost:9200/myindex/order? replication=async'
  • version 容许我们使用乐观锁来控制文档的并发操作。只有当指定的version和文档的version一致的时候,才会执行索引操作。
    curl -XPOST 'http://localhost:9200/myindex/order?version=2'
  • op_type 用来强制执行create操作,如果id已经存在的话,那么索引操作就会失败
    curl -XPOST 'http://localhost:9200/myindex/order? op_type=create'
  • refresh 容许在索引文档完成以后,执行refresh操作
    curl -XPOST 'http://localhost:9200/myindex/order? refresh=true'
  • timeout 定义了主分片可用的超时时间,因为在一些时候,主分片处于不可写的状态(比如正在恢复)这个时候就可以使用这个参数来定义超时时间:
    curl -XPOST 'http://localhost:9200/myindex/order?timeout=5m'

获取文档

curl -XGET http://<server>/<index_name>/<type_name>/<id>
curl -XGET 'http://localhost:9200/myindex/order/2qLrAfPVQvCRMe7Ku8r0Tw?pretty=true'

执行成功以后,es的返回结果的解构如下:

{
    "_index": "myindex",
    "_type": "order",
    "_id": "2qLrAfPVQvCRMe7Ku8r0Tw",
    "_version": 1,
    "found": true,
    "_source": {
        "id": "1234",
        "date": "2013-06-07T12:14:54",
        "customer_id": "customer1",
        "sent": true,
        "items": [
            {
                "name": "item1",
                "quantity": 3,
                "vat": 20
            },
            {
                "name": "item2",
                "quantity": 2,
                "vat": 20
            },
            {
                "name": "item3",
                "quantity": 1,
                "vat": 10
            }
        ]
    }
}

从返回结果可以看到:

  • _source 这个字段包含了我们索引的文档
  • _index 存储文档的索引
  • _type 存储文档的类型
  • _id 文档的id
  • _version 文档当前的版本号
  • found 是否查找到了文档

如果没有找到文档化,那么es的返回结果类似于:

{
    "_id": "2qLrAfPVQvCRMe7Ku8r0Tw",
    "_index": "myindex",
    "_type": "order",
    "found": false
}

在es中,基于id的get调用非常快,因为Elasticsearch仅在包含文档的分片上执行搜索无需其他开销,并且文档ID通常会缓存在内存中,以便快速查找。仅当_source字段被存储(Elasticsearch中的默认设置)时,文档的来源才可用。
同时get操作有一些非常有用的选项需要介绍一下:

  • fields 这个参数容许你指定某些字段,这样可以降低网络资源的占用
    curl 'http://localhost:9200/myindex/order/ 2qLrAfPVQvCRMe7Ku8r0Tw?fields=date,sent'
  • routing 这个选项容许你指定执行get操作的分片,要搜索文档,索引时使用的路由必须与搜索时使用的值相同
    curl 'http://localhost:9200/myindex/order/ 2qLrAfPVQvCRMe7Ku8r0Tw?routing=customer_id'
  • refresh 这个选项容许你在执行get操作之前refresh文档所在的分片,但是这个参数要小心使用,因为他会影响文档索引的效率
    curl http://localhost:9200/myindex/order/ 2qLrAfPVQvCRMe7Ku8r0Tw?refresh=truep
  • preference 容许指定哪些分片副本被选中来执行get操作,通常情况下es都是随机选择的,这个选项的可选值为:
    • _primary 使用主分片
    • _local 首先尝试本地的分配,然后在随机选择。使用本地的分片可以减少带宽的使用。通常应使用自动重新分片(replica set to 0-all).
    • custom value 用于选择分片相关值(如customer_id和username)

但是有一些使用仅仅为了查询文档是否存在,那么可以使用下面的方式:

curl -i -XHEAD 'http://localhost:9200/myindex/order/1'

如果存在的话,http状态码是200,如果不存在的话,http状态码是404
同时,es针对get操作提供了一个endpoint:_source:,使用它可以仅仅返回文档的source字段:

curl -XGET http://localhost:9200/myindex/order/2qLrAfPVQvCRMe7Ku8r0Tw/_source

删除文档

curl -XDELETE 'http://localhost:9200/myindex/order/2qLrAfPVQvCRMe7Ku8r0Tw'

如果删除成功:

{
    "found": true,
    "_index": "myindex",
    "_type": "order",
    "_id": "2qLrAfPVQvCRMe7Ku8r0Tw",
    "_version": 4,
    "result": "deleted",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    }
}

如果文档不存在:

{
    "found": false,
    "_index": "myindex",
    "_type": "order",
    "_id": "2qLrAfPVQvCRMe7Ku8r0Tw",
    "_version": 5,
    "result": "not_found",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    }
}

更新文档

curl -XPOST
'http://localhost:9200/myindex/order/2qLrAfPVQvCRMe7Ku8r0Tw/ _update?pretty' -d '
{
    "script": {
        "inline": "ctx._source.in_stock_items += params.count",
        "params": {
            "count": 4
        }
    }
}
'

如果更新成功,es返回结果:

{
    "_index": "myindex",
    "_type": "order",
    "_id": "2qLrAfPVQvCRMe7Ku8r0Tw",
    "_version": 2,
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    }
}

同时有一个比较有用的属性就是upsert,如果属性不存在,那么就设置他。

curl -XPOST 'http://localhost:9200/myindex/order/2qLrAfPVQvCRMe7Ku8r0Tw/_update' -d '
{
    "script": {
        "inline": "ctx._source.in_stock_items += params.count",
        "params": {
            "count": 4
        }
    },
    "upsert": {
        "in_stock_items": 4
    }
}'

如果你想替换一些字段的值,最好不要写太复杂的update脚本,这个时候可以使用doc,它可以容许我们覆盖一些字段的值:

curl -XPOST 'http://localhost:9200/myindex/order/2qLrAfPVQvCRMe7Ku8r0Tw/_update' -d '
{
    "doc": {
        "in_stock_items": 10
    }
}'

当使用这种方式的时候,如果源文档丢失,我们还可以提供一个doc_as_upsert参数:

curl -XPOST 'http://localhost:9200/myindex/order/2qLrAfPVQvCRMe7Ku8r0Tw/_update' -d '
{
    "doc": {
        "in_stock_items": 10
    },
    "doc_as_upsert": true
}
'

但使用脚本的时候,也可以对字段做一些高级操作,比如:

  • 删除某个字段
    "script" : {"inline": "ctx._source.remove("myfield"}}
    
  • 增加某个字段
    "script" : {"inline": "ctx._source.myfield=myvalue"}}
    

批量操作

为了批量执行操作,我们首先需要吧create/index/delete/update的命令收集在一起,每一行必须以\n结尾,比如:

{ "index":{ "_index":"myindex", "_type":"order", "_id":"1"} }
{ "field1" : "value1", "field2" : "value2" }
{ "delete":{ "_index":"myindex", "_type":"order", "_id":"2" } }
{ "create":{ "_index":"myindex", "_type":"order", "_id":"3" } }
{ "field1" : "value1", "field2" : "value2" }
{ "update":{ "_index":"myindex", "_type":"order", "_id":"3" } }
{ "doc":{"field1" : "value1", "field2" : "value2"}}

然后使用下面的命令发送请求:

curl -s -XPOST localhost:9200/_bulk --data-binary @bulkdata;

从上面的例子可以看出,对于indexcreate以及update操作,需要额外的一行来放置数据,而对于delete操作一行就ok了。
为了在并发情况下能给控制重试此处,批量更新定义了_retry_on_conflict参数来控制在抛出异常之前的重试次数:
在标准配置中,Elasticsearch将HTTP调用限制为100 MB。 如果大小超过限制,则拒绝该执行。
多个复杂的命令需要大量的时间来处理,所以请注意客户端超时。使用批量处理的时候只放置很少的命令并不会提高性能。
如果文件不大,500个命令是一个很好的开始,当然了,你也可以根据数据结构(字段数,嵌套对象数,字段的复杂度等)进行调整。

multi GET操作

批量get操作的url为:

http://<server</_mget
http://<server>/<index_name>/_mget
http://<server>/<index_name>/<type_name>/_mget

具体的例子比如:

curl -XPOST 'localhost:9200/_mget' -fd '
{
    "docs": [
        {
            "_index": "myindex",
            "_type": "order",
            "_id": "2qLrAfPVQvCRMe7Ku8r0Tw"
        },
        {
            "_index": "myindex",
            "_type": "order",
            "_id": "2"
        }
    ]
}'

当然了,如果索引和类型都是一样的话,可以简写为:

curl 'localhost:9200/test/type/_mget' -d '{ "ids" : ["1", "2"] }'
本文版权归作者所有,禁止一切形式的转载,复制等操作
赞赏

微信赞赏支付宝赞赏

发表评论

电子邮件地址不会被公开。 必填项已用*标注