ElasticSearch学习(二)--ElasticSearc
开始第一步
让我们建立一个员工目录
我们首先要做的是存储员工数据,每个文档(document)(行)代表一个员工。所以为了创建员工目录,我们将进行如下操作:
- 为每个员工的文档(document)建立索引,每个文档包含了相应员工的所有信息。
- 每个文档的类型为employee
- employee类型归属于索引megacorp
- megacorp索引存储在ElasticSearch集群中。
实际上这些都是很容易的,我们通过一个命令执行完成操作。
curl -XPUT "localhost:9200/megacorp/employee/1?pretty" -d '{
"first_name":"John",
"last_name":"Smith",
"age":25,
"about":"I love to go rock climbing",
"interests":["sports","music"]
}'
注:在Windows下命令如下:
curl -H "application/json" -XPUT "localhost:9200/megacorp/employee/1?pretty" -d "{
\ "first_name\":\"John\",
\"last_name\":\"Smith\",
\ "age\":25,
\"about\":\"I love to go rock climbing\",
\ "interests\":[\"sports\",\"music\"]
}"
自增ID
curl -XPOST "localhost:9200/website/blog/" -d '{
"title" : "My first blog entry",
"text" : "Just trying this out...",
"date" : "2014/01/01"
}'
响应如下:
{
"_index" : "website",
"_type" : "blog",
"_id" : "X7cGE2UB2XL2Aw5Pme58",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 2,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
检索文档
curl -XGET "localhost:9200/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"
}
}
检索文档的一部分
通常,GET请求将返回文档的全部,存储在_source参数中。但是你可能感兴趣的字段只是title。请求个别字段可以使用_source参数。多个字段可以使用逗号分隔:
curl -i -XGET "localhost:9200/website/blog/123?_source=title,text&pretty"
_source字段现在只包含我们请求的字段,而且过滤了date字段:
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 197
{
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 1,
"found" : true,
"_source" : {
"text" : "Just trying this out...",
"title" : "My first blog entry"
}
}
或者你只想得到_source字段而不要其他的元数据,你可以这样请求:
curl -i -XGET "localhost:9200/website/blog/123/_source?pretty"
它仅仅返回:
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 101
{
"title" : "My first blog entry",
"text" : "Just trying this out...",
"date" : "2014/01/01"
}
检查文档是否存在
如果你想做的只是检查文档是否存在——-你对内容完全不感兴趣,那就使用HEAD方法代替GET,HEAD请求不会返回响应体。只有HTTP头:
curl -i -XHEAD "localhost:9200/website/blog/123?pretty"
ElasticSearch将会返回 200OK 状态(如果你的文档存在):
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 224
如果不存在返回 404 Not Found:
HTTP/1.1 404 Not Found
content-type: application/json; charset=UTF-8
content-length: 83
当然,这只代表你在查询的那一刻文档不存在,但并不带表几毫秒后依旧不存在。另一进程在这期间可能创建新文档。
更新整个文档
文档在ElasticSearch中是不可变的——–我们不能修改他们。
curl -i -XPUT "localhost:9200/website/blog/123" -d '{
"title":"My first blog entry",
"text":"I am starting to get the hang of this...",
"date":"2014/01/02"
}'
在响应中,我们可以看到ElasticSearch把_version增加了。
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 223
{
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 2,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
在内部,ElasticSearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。ElasticSearch会在你继续索引更多数据时清理被删除的文档。
创建一个新文档
当索引一个文档,我们如何确定是完全创建了一个新的还是覆盖了一个已经存在的呢?
请记住_index,_type,_id三者唯一确定一个文档。所以要想保证文档是新加入的,最简单的方式是使用POST方法让ElasticSearch自动生成唯一_id
curl -i -XPOST "localhost:9200/website/blog/" -d '{
....
}'
然而,如果想使用自定义的_id,我们必须告诉ElasticSearch应该在_index,_type,_id三者不同时才接受请求。为了做到这点有两种方法,他们其实做的是同一件事。你可以选择适合自己的方式:
第一种方法使用 op_type 查询参数:
curl -XPUT "localhost:9200/website/blog/123?op_type=create" -d '{
........
}'
第二种方法是在URL后加 /_create 作为端点
curl -XPUT "localhost:9200/website/blog/123/_create" -d '{
........
}'
如果请求成功的创建了一个新文档,ElasticSearch将返回正常的元数据且响应状态吗是201 Created。
另一方面,如果包含相同的 _index,_type,_id的文档已经存在,ElasticSearch将返回409 Conflict响应状态码,错误信息如下
{
"error" : {
"root_cause" : [
{
"type" : "version_conflict_engine_exception",
"reason" : "[blog][123]: version conflict, document already exists (current version [2])",
"index_uuid" : "U3-JJt16SKmUn6rBuBDOfw",
"shard" : "0",
"index" : "website"
}
],
"type" : "version_conflict_engine_exception",
"reason" : "[blog][123]: version conflict, document already exists (current version [2])",
"index_uuid" : "U3-JJt16SKmUn6rBuBDOfw",
"shard" : "0",
"index" : "website"
},
"status" : 409
}
删除文档
删除文档的语法模式与之前基本一致,只不过要使用 DELETE 方法:
curl -XDELETE "localhost:9200/website/blog/123"
如果文档被找到,ElasticSearch将返回 200OK 状态码, _version 数字增加;如果文档未找到,我们将得到一个404 Not Found 状态码,尽管文档不存在,_version 依旧增加了,这是内部记录的一部分,它确保在多节点间不同操作可以有正确的顺序。
文档局部更新
最简单的 update 请求表单接受一个局部文档参数doc,它会合并到现有文档中------对象合并在一起,存在的标量字段被覆盖,新字段被添加。
curl -XPOST "localhost:9200/website/blog/1/_update" -d '{
"doc":{
"tags":["testing"],
"views":0
}
}'
如果请求成功,我们将看到类似请求的响应结果:
{
"_index":"website",
"_id":"1",
"_type":"blog",
"_version":3
}
检索文档显示被更新的_source字段:
{
"_index":"website",
"_id":"1",
"_type":"blog",
"_version":3
"found":true,
"_source":{
"title":"My first blog entry",
"text":"Starting to get the hang of this...",
"tags":["testing"],
"views":0
}
}
我们新添加的字段已经被添加到_source字段中。
检索多个文档
ElasticSearch检索多个文档依旧非常快。合并多个请求可以避免每个请求单独的网络开销。如果你需要从ElasticSearch中检索多个文档,相对于一个一个的检索,更快的方式是在一个请求中使用multi-get或者mget API。
mget API参数是一个 docs 数组,数组的每个节点定义一个文档的 _index,_type,_id 元数据。如果你只想检索一个或几个确定的字段,也可以定义一个 _source 参数:
curl -i -XGET "localhost:9200/_mget" -d '{
"docs":[
{
"_index":"megacorp",
"_type":"employee",
"_id":2
},{
"_index":"megacorp",
"_type":"employee",
"_id":3,
"_source":"age"
}
]
}'
响应体如下:
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 545
{
"docs" : [
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_version" : 2,
"found" : true,
"_source" : {
"first_name" : "Douglas",
"last_name" : "Fir",
"age" : 35,
"about" : "I like to build cabinets",
"interests" : [
"forestry"
]
}
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "3",
"_version" : 1,
"found" : true,
"_source" : {
"age" : 32
}
}
]
}
如果你想检索的文档在同一个 _index 中(甚至在同一个 _type 中),你就可以在URL中定义一个默认的 _index 或者 /_index/_type。
你依旧可以在单独的请求中使用这些值:
curl -i -XGET "192.168.6.101:9200/megacorp/employee/_mget" -d '{
"docs":[
{"_id":2},
{"_index":"website","_type":"blog","_id":123}
]
}'
结果如下:
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 655
{
"docs" : [
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_version" : 2,
"found" : true,
"_source" : {
"first_name" : "Douglas",
"last_name" : "Fir",
"age" : 35,
"about" : "I like to build cabinets",
"interests" : [
"forestry"
]
}
},
{
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 2,
"found" : true,
"_source" : {
"title" : "My first blog entry",
"text" : "I am starting to get the hang of this...",
"date" : "2014/01/02"
}
}
]
}
事实上,如果所有文档具有相同的 _index 和 _type,你可以通过简单的 ids 数组来代替完整的 docs 数组:
curl -i -XGET "192.168.6.101:9200/megacorp/employee/_mget" -d '{
"ids":["2","3"]
}'
更省时的批量操作
就像 mget 允许我们一次性检索多个文档一样,bulk API允许我们使用单一请求来实现多个文档的create,index,delete,update操作。可以以成百上千的数据为一个批次按序进行操作。
bulk 的请求体如下所示,它有一点不同寻常。
{action : {metadata}}\n
{request body}\n
{action:{metadata}}\n
{request body}\n
.........
这种格式类似于用"\n"符号连接起来的一行一行的JSON文档流,两个重要的点需要注意:
- 每行必须以"\n"结尾,包括最后一行。
- 每行不能包含未被转义的换行符,这意味着JSON不能被美化打印
action/metadata定义了文档行为发生在了哪个文档之上。
行为(action)必须是以下几种:
行为 | 解释 |
---|---|
create | 当文档不存在时创建 |
index | 创建新文档或替换已有文档 |
update | 局部更新文档 |
delete | 删除一个文档 |
在索引,更新,删除,创建一个文档时必须指定文档的_index,_type,_id这些元数据。
例如删除请求看起来像这样:
{"delete":{"_index":"megacorp","_type","employee","_id","2"}}
请求体(request body)由文档的 _source组成-------文档包含的一些字段以及其值。它被index和create操作所必须,这是有道理的:你必须提供文档用来索引。
{"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 请求表单是这样的:
curl -i -XPOST "localhost:9200/_bulk" -d '
{"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 update blog post"}}
'
你可以在同一个 index 下的同一个 type 里批量索引日志数据。为每个文档指定相同的元数据是多余的。就像 mget API,bulk 请求也可以在URL中使用 /_index 或 /_index/_type:
curl -i -XPOST "localhost:9200/website/_bulk" -d '
{"index":{"_type":"log"}}
{"event":"User logged in"}
'
你依旧可以覆盖元数据行的 _index和 _type,在没有覆盖时它会使用URL中的值作为默认值:
curl -i -XPOST "localhost:9200/website/log/_bulk" -d '
{"index":{}}
{"event":"User logged in"}
{"index":{"_type":"blog"}}
{"title":"Overriding the default type"}
'