Elasticsearch---读书笔记(2)

280 阅读6分钟

二 基础入门

3、数据输入和输出

3.1、什么是文档

  • 多数实体或对象可以被序列化为包含键值对的 JSON 对象
{
    "name":         "John Smith",
    "age":          42,
    "confirmed":    true,
    "join_date":    "2014-06-01",
    "home": {
        "lat":      51.5,
        "lon":      0.1
    },
    "accounts": [
        {
            "type": "facebook",
            "id":   "johnsmith"
        },
        {
            "type": "twitter",
            "id":   "johnsmith"
        }
    ]
}

通常情况下,我们使用的术语 对象 和 文档 是可以互相替换的

3.2 文档元数据

  • 三个必须的元数据元素如下:

    • _index : 文档在哪存放

    • _type: 文档表示的对象类别

    • _id :文档唯一标识

3.2.1. _index

  • 一个 索引 应该是因共同的特性被分组到一起的文档集合。

  • 例如,你可能存储所有的产品在索引 products 中,而存储所有销售的交易到索引 sales 中。 虽然也允许存储不相关的数据到一个索引中,但这通常看作是一个反模式的做法。

3.2.2. _type

  • 数据可能在 索引 中只是松散的组合在一起,但是通常明确定义一些数据中的子分区是很有用的。
  • 例如,所有的产品都放在一个索引中,但是你有许多不同的产品类别,比如 "electronics" 、 "kitchen" 和 "lawn-care"。

3.2.3. _id

  • ID 是一个字符串,当它和 _index 以及 _type 组合就可以唯一确定 Elasticsearch 中的一个文档。 当你创建一个新的文档,要么提供自己的 _id ,要么让 Elasticsearch 帮你生成。

3.3、 索引文档

通过使用 index API ,文档可以被 索引 —— 存储和使文档可被搜索。 但是首先,我们要确定文档的位置。正如我们刚刚讨论的,一个文档的 _index 、 _type 和 _id 唯一标识一个文档。 我们可以提供自定义的 _id 值,或者让 index API 自动生成。

3.3.1、 使用自定义的 ID

如果你的文档有一个自然的标识符 (例如,一个 user_account 字段或其他标识文档的值),你应该使用如下方式的 index API 并提供你自己 _id :

PUT /{index}/{type}/{id}
{
  "field": "value",
  ...
}

举个例子,如果我们的索引称为 website ,类型称为 blog ,并且选择 123 作为 ID ,那么索引请求应该是下面这样:

PUT /website/blog/123
{
  "title": "My first blog entry",
  "text":  "Just trying this out...",
  "date":  "2014/01/01"
}

Elasticsearch 响应体如下所示:

{
   "_index":    "website",
   "_type":     "blog",
   "_id":       "123",
   "_version":  1,
   "created":   true
}

该响应表明文档已经成功创建,该索引包括 _index 、 _type 和 _id 元数据, 以及一个新元素: _version

3.3.2、 Autogenerating IDs

  • 自动生成的 ID 是 URL-safe、 基于 Base64 编码且长度为20个字符的 GUID 字符串

3.4、 取回一个文档

GET /website/blog/123?pretty
{
  "_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"
  }
}

3.4.1. 返回文档的一部分

GET /website/blog/123?_source=title,text
{
  "_index" :   "website",
  "_type" :    "blog",
  "_id" :      "123",
  "_version" : 1,
  "found" :   true,
  "_source" : {
      "title": "My first blog entry" ,
      "text":  "Just trying this out..."
  }
}

3.5、 检查文档是否存在

curl -i -XHEAD http://localhost:9200/website/blog/123
  • 正确返回200
  • 错误返回404

3.6. 更新整个文档

PUT /website/blog/123
{
  "title": "My first blog entry",
  "text":  "I am starting to get the hang of this...",
  "date":  "2014/01/02"
}

在响应体中,我们能看到 Elasticsearch 已经增加了 _version 字段值:

{
  "_index" :   "website",
  "_type" :    "blog",
  "_id" :      "123",
  "_version" : 2,
  "created":   false (1)
}
  1. created 标志设置成 false ,是因为相同的索引、类型和 ID 的文档已经存在。
  • 在内部,Elasticsearch 已将旧文档标记为已删除,并增加一个全新的文档。 尽管你不能再对旧版本的文档进行访问,但它并不会立即消失。当继续索引更多的数据,Elasticsearch 会在后台清理这些已删除文档。

  • 在本章的后面部分,我们会介绍 update API, 这个 API 可以用于部分更新。 虽然它似乎对文档直接进行了修改,但实际上 Elasticsearch 按前述完全相同方式执行以下过程:

  1. 从旧文档构建 JSON
  2. 更改该 JSON
  3. 删除旧文档
  4. 索引一个新文档
  • 唯一的区别在于, update API 仅仅通过一个客户端请求来实现这些步骤,而不需要单独的 get 和 index 请求。

3.7. 创建新文档

当我们索引一个文档,怎么确认我们正在创建一个完全新的文档,而不是覆盖现有的呢?

请记住, _index 、 _type 和 _id 的组合可以唯一标识一个文档。所以,确保创建一个新文档的最简单办法是,使用索引请求的 POST 形式让 Elasticsearch 自动生成唯一 _id :

POST /website/blog/
{ ... }

然而,如果已经有自己的 _id ,那么我们必须告诉 Elasticsearch ,只有在相同的 _index 、 _type 和 _id 不存在时才接受我们的索引请求。这里有两种方式,他们做的实际是相同的事情。使用哪种,取决于哪种使用起来更方便。

第一种方法使用 op_type 查询-字符串参数:

PUT /website/blog/123?op_type=create
{ ... }

第二种方法是在 URL 末端使用 /_create :

PUT /website/blog/123/_create
{ ... }

如果创建新文档的请求成功执行,Elasticsearch 会返回元数据和一个 201 Created 的 HTTP 响应码。

另一方面,如果具有相同的 _index 、 _type 和 _id 的文档已经存在,Elasticsearch 将会返回 409 Conflict 响应码,以及如下的错误信息:

{
   "error": {
      "root_cause": [
         {
            "type": "document_already_exists_exception",
            "reason": "[blog][123]: document already exists",
            "shard": "0",
            "index": "website"
         }
      ],
      "type": "document_already_exists_exception",
      "reason": "[blog][123]: document already exists",
      "shard": "0",
      "index": "website"
   },
   "status": 409
}

3.8. 删除文档

删除文档的语法和我们所知道的规则相同,只是使用 DELETE 方法:

DELETE /website/blog/123

如果找到该文档,Elasticsearch 将要返回一个 200 ok 的 HTTP 响应码,和一个类似以下结构的响应体。注意,字段 _version 值已经增加:

{
  "found" :    true,
  "_index" :   "website",
  "_type" :    "blog",
  "_id" :      "123",
  "_version" : 3
}

如果文档没有找到,我们将得到 404 Not Found 的响应码和类似这样的响应体:

{
  "found" :    false,
  "_index" :   "website",
  "_type" :    "blog",
  "_id" :      "123",
  "_version" : 4
}

即使文档不存在( Found 是 false ), _version 值仍然会增加。这是 Elasticsearch 内部记录本的一部分,用来确保这些改变在跨多节点时以正确的顺序执行。

3.9. 处理冲突

  • 多个客户端同时修改的时候,会产生冲突
  • 可以通过乐观锁和悲观锁进行控制
  • 在 es 主要通过乐观锁进行控制

3.10. 乐观并发控制

  • 通过版本号的 cas 操作

3.11. 文档的部分更新

  • 使用 update API 我们还可以部分更新文档

3.12. 取回多个文档

mget API 要求有一个 docs 数组作为参数,每个元素包含需要检索文档的元数据, 包括 _index 、 _type 和 _id 。如果你想检索一个或者多个特定的字段,那么你可以通过 _source 参数来指定这些字段的名字:

GET /_mget
{
   "docs" : [
      {
         "_index" : "website",
         "_type" :  "blog",
         "_id" :    2
      },
      {
         "_index" : "website",
         "_type" :  "pageviews",
         "_id" :    1,
         "_source": "views"
      }
   ]
}