【es】文档的简单入门

148 阅读4分钟

文档的简单入门

更新整个文档

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
         }
      }
   ]
}

\