02-Elasticsearch API - 文档操作
ES中文档的概念
ES中一个文档即为一个json格式的文本,隶属于某个index的某个type下。当索引进ES后,一个文档不仅包含原始文档信息,还包含一下元数据:
_index 文档存放在那个索引
_type 文档属于什么对象类别
_id 文档的唯一标识
索引一个文档(新建一个文档)
自己根据业务指定自定义id
PUT /{index}/{type}/{id}
{
"field": "value",
...
}
实例:
PUT /website/blog/123
{
"title": "My first blog entry",
"text": "Just trying this out...",
"date": "2014/01/01"
}
//result
{
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
让ES为我们自动生成ID
POST /{index}/{type}
{
"field": "value",
...
}
实例
POST /website/blog/
{
"title": "My second blog entry",
"text": "Still trying this out...",
"date": "2014/01/01"
}
//result
{
"_index": "website",
"_type": "blog",
"_id": "AV1e6wxyr-eh0mA78TXL",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
取回一个文档
GET /{index}/{type}/{id}
实例:
//在请求的查询串参数中加上 pretty 参数, 正如前面的例子中看到的,这将会调用 Elasticsearch 的 pretty-print 功能,该功能 使得 JSON 响应体更加可读。但是, _source 字段不能被格式化打印出来。相反,我们得到的 _source 字段中的 JSON 串,刚好是和我们传给它的一样。
GET /website/blog/123?pretty
//result
{
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 1,
"found": true,
"_source": {
"title": "My first blog entry",
"text": "Just trying this out...",
"date": "2014/01/01"
}
}
返回文档的一部分
GET /{index}/{type}/{id}?_source=field1,field2...
实例:
GET /website/blog/123?_source=title,text
//result
{
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 1,
"found": true,
"_source": {
"text": "Just trying this out...",
"title": "My first blog entry"
}
}
只想得到source字段
GET /{index}/{type}/{id}/_source
实例:
GET /website/blog/123/_source
//result
{
"title": "My first blog entry",
"text": "Just trying this out...",
"date": "2014/01/01"
}
检测文档是否存在
将获取文档的GET请求改为HEAD请求即可,返回体只会返回一个HTTP请求报头
HEAD /{index}/{type}/{id}
返回的头信息:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
更新整个文档
在 ES 中文档是 不可改变 的,不能修改它们。 相反,如果想要更新现有的文档,需要 重建索引 或者进行替换, 我们可以使用相同的 index API 进行实现
实例:
PUT /website/blog/123
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
//result
{
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 2, //版本号增加了
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": false //是否创建为false
}
在内部,Elasticsearch 已将旧文档标记为已删除,并增加一个全新的文档。 尽管你不能再对旧版本的文档进行访问,但它并不会立即消失。当继续索引更多的数据,Elasticsearch 会在后台清理这些已删除文档。
控制只是创建文档不会更新文档
当使用POST不指定id时,ES始终都是新建文档,当指定id时可以使用一下两种方式控制新建,当id存在时,则返回失败
PUT /{index}/{type}/{id}?op_type=create
{...}
PUT /{index}/{type}/{id}/_create
{...}
删除文档
DELETE /{index}/{type}/{id}
实例:
DELETE /website/blog/123
//result
{
"found": true,
"_index": "website",
"_type": "blog",
"_id": "123",
"_version": 3,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
文档的部分更新
我们也介绍过文档是不可变的:他们不能被修改,只能被替换。 update API 必须遵循同样的规则。 从外部来看,我们在一个文档的某个位置进行部分更新。然而在内部, update API 简单使用与之前描述相同的 检索-修改-重建索引 的处理过程。 区别在于这个过程发生在分片内部,这样就避免了多次请求的网络开销。通过减少检索和重建索引步骤之间的时间,我们也减少了其他进程的变更带来冲突的可能性。
POST /{index}/{type}/{id}/_update
{
"doc": {
"field": value,
...
}
}
实例:
POST /website/blog/1/_update
{
"doc" : {
"tags" : [ "testing" ],
"views": 0
}
}
//result
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
使用Groovy脚本更新文档
//将views字段值加1
POST /website/blog/1/_update
{
"script" : "ctx._source.views+=1"
}
//result
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 3,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
取回多个文档
跨index与type取回文档
GET /_mget
GET /_mget
{
"docs" : [
{
"_index" : {index},
"_type" : {type},
"_id" : {id}
},
...
]
}
实例:
GET /_mget
{
"docs" : [
{
"_index" : "website",
"_type" : "blog",
"_id" : 1
},
{
"_index" : "website",
"_type" : "pageviews",
"_id" : 1,
"_source": "views"
}
]
}
//result
{
"docs": [
{
"_index": "website",
"_type": "blog",
"_id": "1",
"_version": 3,
"found": true,
"_source": {
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02",
"views": 1,
"tags": [
"testing"
]
}
},
{
"_index": "website",
"_type": "pageviews",
"_id": "1",
"found": false
}
]
}
如果想检索的数据都在相同的 _index 中(甚至相同的 _type 中),则可以在 URL 中指定默认的 /_index 或者默认的 /_index/_type
实例:
GET /website/blog/_mget
{
"docs" : [
{ "_id" : 2 },
{ "_type" : "pageviews", "_id" : 1 }
]
}
如果所有文档的 _index 和 _type 都是相同的,你可以只传一个 ids 数组
实例:
GET /website/blog/_mget
{
"ids" : [ "2", "1" ]
}
代价较小的批量操作
与 mget 可以使我们一次取回多个文档同样的方式, bulk API 允许在单个步骤中进行多次 create 、 index 、 update 或 delete 请求。
POST /_bulk
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
...
这种格式类似一个有效的单行 JSON 文档 流 ,它通过换行符(\n)连接到一起。注意两个要点:
每行一定要以换行符(\n)结尾, 包括最后一行 。这些换行符被用作一个标记,可以有效分隔行。
这些行不能包含未转义的换行符,因为他们将会对解析造成干扰。这意味着这个 JSON 不 能使用 pretty 参数打印。
action/metadata 行指定 哪一个文档 做 什么操作 。
action 必须是以下选项之一:
create
如果文档不存在,那么就创建它。详情请见 创建新文档。
index
创建一个新文档或者替换一个现有的文档。详情请见 索引文档 和 更新整个文档。
update
部分更新一个文档。详情请见 文档的部分更新。
delete
删除一个文档。详情请见 删除文档。
metadata 应该 指定被索引、创建、更新或者删除的文档的 _index 、 _type 和 _id 。
实例:
POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "My first blog post" }
{ "index": { "_index": "website", "_type": "blog" }}
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
{ "doc" : {"title" : "My updated blog post"} }
多大是太大了?
整个批量请求都需要由接收到请求的节点加载到内存中,因此该请求越大,其他请求所能获得的内存就越少。 批量请求的大小有一个最佳值,大于这个值,性能将不再提升,甚至会下降。 但是最佳值不是一个固定的值。它完全取决于硬件、文档的大小和复杂度、索引和搜索的负载的整体情况。
幸运的是,很容易找到这个 最佳点 :通过批量索引典型文档,并不断增加批量大小进行尝试。 当性能开始下降,那么你的批量大小就太大了。一个好的办法是开始时将 1,000 到 5,000 个文档作为一个批次, 如果你的文档非常大,那么就减少批量的文档个数。
密切关注你的批量请求的物理大小往往非常有用,一千个 1KB 的文档是完全不同于一千个 1MB 文档所占的物理大小。 一个好的批量大小在开始处理后所占用的物理大小约为 5-15 MB。