Elasticserch学习之数据

一、索引

1、使用自己的ID

例如我们的索引叫做“website”,类型叫做“blog”,我们选择的ID是“123”,那么这个索引请求就像这样:

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

2、自增ID

URL现在只包含_index_type两个字段:

POST /website/blog/
{
  "title": "My second blog entry",
  "text":  "Still trying this out...",
  "date":  "2014/01/01"
}

二、获取

1、检索文档

GET /website/blog/123?pretty

pretty

在任意的查询字符串中增加 pretty 参数,类似于上面的例子。会让Elasticsearch美化输出(pretty-print)JSON响应以便更加容易阅读。_source字段不会被美化,它的样子与我们输入的一致。

2、检索文档的一部分

GET /website/blog/123?_source=title,text

_source字段现在只包含我们请求的字段,而且过滤了date字段:

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

或者你只想得到_source字段而不要其他的元数据,你可以这样请求:

GET /website/blog/123/_source

它仅仅返回:

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

三、检查文档是否存在

HEAD /website/blog/123

Elasticsearch将会返回 200 OK 状态如果你的文档存在:

200 - OK

如果不存在返回 404 Not Found:

HEAD /website/blog/124
404 - Not Found

当然,这只表示你在查询的那一刻文档不存在,但并不表示几毫秒后依旧不存在。另一个进程在这期间可能创建新文档。

四、更新整个文档

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" : 3,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1
}

在内部,Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。

五、创建一个新文档

1、最简单的方式是使用POST方法让Elasticsearch自动生成唯一 _id

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

2、使用op_type查询参数

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

3、在URL后加/_create做为端点

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

六、删除文档

删除文档的语法模式与之前基本一致,只不过要使用DELETE方法:

DELETE /website/blog/124

如果文档被找到,Elasticsearch将返回200 OK状态码和以下响应体。注意_version数字已经增加了。

{
  "_index" : "website",
  "_type" : "blog",
  "_id" : "125",
  "_version" : 2,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1
}

 

如果文档未找到,我们将得到一个404 Not Found状态码,响应体是这样的:

{
  "_index" : "website",
  "_type" : "blog",
  "_id" : "127",
  "_version" : 1,
  "result" : "not_found",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

 

尽管文档不存在——"found"的值是false——_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,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

现在我们更新这个文档,指定一个新version号码为10

PUT /website/blog/2?version=10&version_type=external
{
  "title": "My first external blog entry",
  "text":  "This is a piece of cake..."
}

请求成功:

{
  "_index" : "website",
  "_type" : "blog",
  "_id" : "2",
  "_version" : 10,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}

八、局部更新

示例:为博客添加一个tags字段和一个views字段。

POST /website/blog/1/_update
{
   "doc" : {
      "tags" : [ "testing" ],
      "views": 0
   }
}

请求成功:

{
  "_index" : "website",
  "_type" : "blog",
  "_id" : "1",
  "_version" : 3,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 6,
  "_primary_term" : 1
}

检索文档文档显示被更新的_source字段:

{
        "_index" : "website",
        "_type" : "blog",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "title" : "My first blog entry",
          "text" : "Starting to get the hang of this...",
          "views" : 0,
          "tags" : [
            "testing"
          ]
        }
  }

使用脚本局部更新

使用Groovy脚本:

默认的脚本语言是Groovy,一个快速且功能丰富的脚本语言,语法类似于Javascript。它在一个沙盒(sandbox)中运行,以防止恶意用户毁坏Elasticsearch或攻击服务器。

脚本能够使用update API改变_source字段的内容,它在脚本内部以ctx._source表示。

示例:使用脚本增加博客的views数量。

POST /website/blog/1/_update
{
   "script" : "ctx._source.views+=1"
}

九、Mget

1、检索多个文档

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

如果你想检索的文档在同一个_index中(甚至在同一个_type中),你就可以在URL中定义一个默认的/_index或者/_index/_type

POST /website/blog/_mget
{
   "docs" : [
      { "_id" : 2 },
      { "_type" : "pageviews", "_id" :   1 }
   ]
}

事实上,如果所有文档具有相同_index_type,你可以通过简单的ids数组来代替完整的docs数组:

POST /website/blog/_mget
{
   "ids" : [ "2", "1" ]
}

注意到我们请求的第二个文档并不存在。我们定义了类型为blog,但是ID为1的文档类型为pageviews。这个不存在的文档会在响应体中被告知。

{
  "docs" : [
    {
      "_index" :   "website",
      "_type" :    "blog",
      "_id" :      "2",
      "_version" : 10,
      "found" :    true,
      "_source" : {
        "title":   "My first external blog entry",
        "text":    "This is a piece of cake..."
      }
    },
    {
      "_index" :   "website",
      "_type" :    "blog",
      "_id" :      "1",
      "found" :    false  <1>
    }
  ]
}
  • <1> 这个文档不存在

事实上第二个文档不存在并不影响第一个文档的检索。每个文档的检索和报告都是独立的。

十、更新时的批量操作

就像 mget 允许我们一次性检索多个文档一样,bulk API允许我们使用单一请求来实现多个文档的 createindexupdatedelete。这对索引类似于日志活动这样的数据流非常有用,它们可以以成百上千的数据为一个批次按序进行索引。

bulk请求体如下:

{ action: { metadata }}\n
{ request body        }\n
{ action: { metadata }}\n
{ request body        }\n
...

 这种格式类似于用"\n"符号连接起来的一行一行的JSON文档流(stream)。两个重要的点需要注意:

  • 每行必须以"\n"符号结尾,包括最后一行。这些都是作为每行有效的分离而做的标记。

  • 每一行的数据不能包含未被转义的换行符,它们会干扰分析——这意味着JSON不能被美化打印。

action/metadata 这一行定义了文档行为(what action)发生在哪个文档(which document)之上。

行为(action)必须是以下几种:

行为解释
create 当文档不存在时创建之。详见《创建文档》
index 创建新文档或替换已有文档。见《索引文档》和《更新文档》
update 局部更新文档。见《局部更新》
delete 删除一个文档。见《删除文档》

 

在索引、创建、更新或删除时必须指定文档的_index_type_id这些元数据(metadata)。

例如删除请求看起来像这样:

{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}

这些还被 update 操作所必需,而且请求体的组成应该与update API(docupsertscript等等)一致。删除操作不需要请求体(request body)。

{ "create":  { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "My first blog post" }

如果没有定义_id,ID将会被自动创建:

{ "index": { "_index": "website", "_type": "blog" }}
{ "title":    "My second blog post" }

为了将这些放在一起,bulk请求表单是这样的:

POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }} <1>
{ "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"} } <2>
  • <1> 注意delete行为(action)没有请求体,它紧接着另一个行为(action)。
  • <2> 记得最后一个换行符。

Elasticsearch响应包含一个items数组,它罗列了每一个请求的结果,结果的顺序与我们请求的顺序相同:

{
  "took" : 103,
  "errors" : false,   <1>
  "items" : [
    {
      "delete" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 5,
        "result" : "deleted",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 5,
        "_primary_term" : 1,
        "status" : 200
      }
    },
    {
      "create" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 6,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 6,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "index" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "cP66bGkBUi3i0pMApm5N",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 8,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "update" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 7,
        "result" : "updated",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 7,
        "_primary_term" : 1,
        "status" : 200
      }
    }
  ]
}
  • <1> 所有子请求都成功完成。

每个子请求都被独立的执行,所以一个子请求的错误并不影响其它请求。如果任何一个请求失败,顶层的error标记将被设置为true,然后错误的细节将在相应的请求中被报告:

POST /_bulk
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "Cannot create - it already exists" }
{ "index":  { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title":    "But we can update it" }

响应中我们将看到create文档123失败了,因为文档已经存在,但是后来的在123上执行的index请求成功了:

{
  "took" : 42,
  "errors" : true,  <1>
  "items" : [
    {
      "create" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "status" : 409,  <2>
        "error" : {
          "type" : "version_conflict_engine_exception",  <3>
          "reason" : "[blog][123]: version conflict, document already exists (current version [7])",
          "index_uuid" : "Uc01DQRhSqq_REs9HsSYPA",
          "shard" : "0",
          "index" : "website"
        }
      }
    },
    {
      "index" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 8,
        "result" : "updated",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 8,
        "_primary_term" : 1,
        "status" : 200  <4>
      }
    }
  ]
}
  • <1> 一个或多个请求失败。
  • <2> 这个请求的HTTP状态码被报告为409 CONFLICT
  • <3> 错误消息说明了什么请求错误。
  • <4> 第二个请求成功了,状态码是200 OK

这些说明 bulk 请求不是原子操作——它们不能实现事务。每个请求操作时分开的,所以每个请求的成功与否不干扰其它操作。

不要重复

你可能在同一个index下的同一个type里批量索引日志数据。为每个文档指定相同的元数据是多余的。就像mget API,bulk请求也可以在URL中使用/_index/_index/_type:

POST /website/_bulk
{ "index": { "_type": "log" }}
{ "event": "User logged in" }

你依旧可以覆盖元数据行的_index_type,在没有覆盖时它会使用URL中的值作为默认值:

POST /website/log/_bulk
{ "index": {}}
{ "event": "User logged in" }
{ "index": { "_type": "blog" }}
{ "title": "Overriding the default type" }

 

posted on 2019-03-11 20:23  mentiantian  阅读(168)  评论(0编辑  收藏  举报