文档的简单入门
更新整个文档
es中的文档是不可改变的,如果想要更新现有的文档,需要重建索引或进行替换。
PUT /website/blog/123
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
在响应体中,我们能看到 es 已经增加了 _version 字段值:
{
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 2,
"created": false //created 标志设置成 false ,是因为相同的索引、类型和 ID 的文档已经存在。
}
在es内部的处理中,会将旧文档标记为已删除,并增加一个全新的文档,旧版本的文档不能再被进行访问,也不会立刻消失,当索引中存在更多的数据时,es会在后台清理这些已删除的文档。
更新文档部分内容
从前面的内容我们知道,文档是不可变的,只可以被替换,不能被修改,updateAPI同样遵循这个规则,使用updateAPI对文档进行“修改”,在es内部,updateAPI在对文档实际的操作是:检索-重建索引。
updateAPI与前面更新整个文档的主要区别在于,updateAPI的检索和重建索引操作发生在分片内部,这样避免了多次请求的网络开销,通过减少检索和重建索引步骤之间的时间,减少了其他进程的变更带来冲突的可能性。
update请求最简单的一种形式是接收文档的一部分作为doc的参数,它会与现有的文档进行合并,对象被合并到一起,覆盖现有的字段,增加新的字段。
POST /website/blog/1/_update
{
"doc" : {
"tags" : [ "testing" ], //增加字段tags和views
"views": 0
}
}
如果请求成功,可以看到类似的响应:
{
"_index" : "website",
"_id" : "1",
"_type" : "blog",
"_version" : 3
}
更新时的冲突
我们前面讲到检索和重建索引步骤的间隔越小,变更冲突的机会越小。但是也不能完全消除这种可能性。
这里update在检索步骤时检索得到文档当前的_version时,在重建索引步骤时,会将版本号一起传递过去。如果另一个进程修改了处于检索和重建索引步骤之间的文档,那么_version号不匹配,更新则会失败。
有些场景对于操作发生的先后顺序不太关注,这个时候我们可以通过设置参数retry_on_conflict来自动完成,它的默认值为0。
并发控制
内部版本号
es中使用的乐观锁思想进行并发控制。
es中,每一个文档都有一个唯一的_version版本号,每当文档被修改时,版本号都会递增。如果旧版本号的文档的操作在新版本号之后,旧版本号的操作则会被忽略。
外部版本号
除了自身生成的_version,es还可以指定_version的内容,通过version_type=external来重用这个字段,这里需要注意的点是,_version是有范围的,版本号必须是大于零的整数, 且小于 9.2E+18 — 一个 Java 中 long 类型的正值,且外部版本号的处理方式和内部版本号的处理方式有些不同,es不会检查当前_version和请求中指定的版本号是否相同,而是检查当前_version是否小于指定的版本号。如果请求成功,外部版本号作为文档的新_version进行存储。
例如,要创建一个新的具有外部版本号 5 的博客文章,我们可以按以下方法进行:
PUT /website/blog/2?version=5&version_type=external
{
"title": "My first external blog entry",
"text": "Starting to get the hang of this..."
}
在响应中,我们能看到当前的 _version 版本号是 5 :
{
"_index": "website",
"_type": "blog",
"_id": "2",
"_version": 5,
"created": true
}
现在我们更新这个文档,指定一个新的 version 号是 10 :
PUT /website/blog/2?version=10&version_type=external
{
"title": "My first external blog entry",
"text": "This is a piece of cake..."
}
请求成功并将当前 _version 设为 10 :
{
"_index": "website",
"_type": "blog",
"_id": "2",
"_version": 10,
"created": false
}
取回多个文档
如果想将多个请求合并成一个,避免单独处理每个请求花费的网络延迟和开销,可以使用mgetAPI。
mget API 要求有一个 docs 数组作为参数,每个元素包含需要检索文档的元数据, 包括 _index 、 _type 和 _id 。如果你想检索一个或者多个特定的字段,那么你可以通过 _source 参数来指定这些字段的名字:
GET /_mget
{
"docs" : [
{
"_index" : "website",
"_type" : "blog",
"_id" : 2
},
{
"_index" : "website",
"_type" : "pageviews",
"_id" : 1,
"_source": "views"
}
]
}
该响应体也包含一个 docs 数组, 对于每一个在请求中指定的文档,这个数组中都包含有一个对应的响应,且顺序与请求中的顺序相同。 其中的每一个响应都和使用单个 get request请求所得到的响应体相同:
{
"docs" : [
{
"_index" : "website",
"_id" : "2",
"_type" : "blog",
"found" : true,
"_source" : {
"text" : "This is a piece of cake...",
"title" : "My first external blog entry"
},
"_version" : 10
},
{
"_index" : "website",
"_id" : "1",
"_type" : "pageviews",
"found" : true,
"_version" : 2,
"_source" : {
"views" : 2
}
}
]
}
\