https://es.xiaoleilu.com/030_Data/05_Document.html
《ELasticsearch in Action》
以下的操作在ES7.5版本下。
文档
一个文档不只有数据,还包含了元数据,三个必须的元数据是:
_index: 索引,可以理解为mysql中数据库。
_type: 7.5版本后已经强制单索引单类型。
_id: 创建文档的时候可以指定,也可以不指定,es会自定生成。
检索文档
指定id检索
1 | GET /website/123?pretty |
这里指定了id来检索文档,将会只返回一个结果。同时返回的结果中将包括_source自定,其内容是我们新建文档123的时候发送的全部内容。
检索文档的一部分
1 | GET /website/123?_source=title,text |
返回的source里面将只会有title和text字段。
检查文档知否存在
1 | HEAD /megacorp/_doc/1 |
将返回200 - OK
如果不存在将返回404 - Not Found
更新文档
1 | PUT /website/_doc/123 |
2 | { |
3 | "title": "My first blog entry", |
4 | "text": "I am starting to get the hang of this...", |
5 | "date": "2014/01/02" |
6 | } |
存在将更新,不存在将创建。可以从返回结果的”result” : “updated”,看出
1 | { |
2 | "_index" : "website", |
3 | "_type" : "_doc", |
4 | "_id" : "12", |
5 | "_version" : 2, |
6 | "result" : "updated", |
7 | "_shards" : { |
8 | "total" : 2, |
9 | "successful" : 1, |
10 | "failed" : 0 |
11 | }, |
12 | "_seq_no" : 3, |
13 | "_primary_term" : 1 |
14 | } |
Es的更新文档操作过程:
- 从旧文档中检索JSON
- 修改它
- 删除旧文档
- 索引新文档
创建一个文档
如果想创建一个文档,而不是更新,使用:
1 | POST /website/_doc/12?op_type=create |
或者:
1 | POST /website/_doc/12/_create |
删除一个文档
1 | DELETE /website/_doc/12 |
版本控制
内部版本号
每个文档都有一个_version号码,这个号码在文档被改变时加一。Elasticsearch使用这个_version保证所有修改都被正确排序。当一个旧版本出现在新版本之后,它会被简单的忽略。
我们利用_version的这一优点确保数据不会因为修改冲突而丢失。我们可以指定文档的version来做想要的更改。如果那个版本号不是现在的,我们的请求就失败了。
Let’s create a new blog post: 让我们创建一个新的博文:
1 | PUT /website/_doc/1/_create |
2 | { |
3 | "title": "My first blog entry", |
4 | "text": "Just trying this out..." |
5 | } |
响应体告诉我们这是一个新建的文档,它的_version是1。现在假设我们要编辑这个文档:把数据加载到web表单中,修改,然后保存成新版本。
首先我们检索文档:
1 | GET /website/_doc/1 |
2 | 响应体包含相同的_version是1 |
3 | { |
4 | "_index" : "website", |
5 | "_type" : "blog", |
6 | "_id" : "1", |
7 | "_version" : 1, |
8 | "found" : true, |
9 | "_source" : { |
10 | "title": "My first blog entry", |
11 | "text": "Just trying this out..." |
12 | } |
13 | } |
现在,当我们通过重新索引文档保存修改时,我们这样指定了version参数:
1 | PUT /website/_doc/1?version=1 |
2 | { |
3 | "title": "My first blog entry", |
4 | "text": "Starting to get the hang of this..." |
5 | } |
外部版本号
如果主数据库有版本字段——或一些类似于timestamp等可以用于版本控制的字段——是你就可以在Elasticsearch的查询字符串后面添加version_type=external来使用这些版本号。版本号必须是整数,大于零小于9.2e+18——Java中的正的long。
外部版本号与之前说的内部版本号在处理的时候有些不同。它不再检查_version是否与请求中指定的一致,而是检查是否小于指定的版本。如果请求成功,外部版本号就会被存储到_version中
局部更新
可以使用以下请求为博客添加一个tags字段和一个views字段:
1 | POST /website/blog/1/_update |
2 | { |
3 | "doc" : { |
4 | "tags" : [ "testing" ], |
5 | "views": 0 |
6 | } |
7 | } |
如果文档不存在,将返回404。
更新不奴存在的文档
在这种情况下,我们可以使用upsert参数定义文档来使其不存在时被创建。
1 | POST /website/_update/1 |
2 | { |
3 | "script" : "ctx._source.views+=1", |
4 | "upsert": { |
5 | "views": 1 |
6 | } |
7 | } |
检索多个文档
检索多个文档依旧非常快。合并多个请求可以避免每个请求单独的网络开销。如果你需要从Elasticsearch中检索多个文档,相对于一个一个的检索,更快的方式是在一个请求中使用multi-get或者mget API。
mget API参数是一个docs数组,数组的每个节点定义一个文档的_index、_type、_id元数据。如果你只想检索一个或几个确定的字段,也可以定义一个_source参数:
1 | POST /_mget |
2 | { |
3 | "docs" : [ |
4 | { |
5 | "_index" : "website", |
6 | "_id" : 2 |
7 | }, |
8 | { |
9 | "_index" : "megacorp", |
10 | "_id" : 1, |
11 | "_source": "views" |
12 | } |
13 | ] |
14 | } |
响应体也包含一个docs数组,每个文档还包含一个响应,它们按照请求定义的顺序排列。
1 | { |
2 | "docs" : [ |
3 | { |
4 | "_index" : "website", |
5 | "_type" : "blog", |
6 | "_id" : "2", |
7 | "found" : false |
8 | }, |
9 | { |
10 | "_index" : "megacorp", |
11 | "_type" : "employee", |
12 | "_id" : "1", |
13 | "_version" : 2, |
14 | "_seq_no" : 1, |
15 | "_primary_term" : 1, |
16 | "found" : true, |
17 | "_source" : { } |
18 | } |
19 | ] |
20 | } |
批量操作
为了将这些放在一起,bulk请求表单是这样的:
1 | POST /_bulk |
2 | { "delete": { "_index": "website", "_type": "blog", "_id": "123" }} <1> |
3 | { "create": { "_index": "website", "_type": "blog", "_id": "123" }} |
4 | { "title": "My first blog post" } |
5 | { "index": { "_index": "website", "_type": "blog" }} |
6 | { "title": "My second blog post" } |
7 | { "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} } |
8 | { "doc" : {"title" : "My updated blog post"} } <2> |
<1> 注意delete行为(action)没有请求体,它紧接着另一个行为(action)
<2> 记得最后一个换行符
搜索
为了充分挖掘Elasticsearch的潜力,你需要理解以下三个概念:
映射:Mapping,数据在每个字段中的解释说明
分析:Analysis,全部是如何处理的可以被搜索的
DSL:领域特定语言查询,es中使用的灵活的、强大的查询语言。
空搜索
1 | GET /_search |
将会返回集群中所有文档。
重要:
搜索一个索引有5个主分片和5个索引各有一个分片事实上是一样的。
简易搜索
search API有两种表单:一种是“简易版”的查询字符串(query string)将所有参数通过查询字符串定义,另一种版本使用JSON完整的表示请求体(request body),这种富搜索语言叫做结构化查询语句(DSL)。
_all字段
返回包含”mary”字符的所有文档的简单搜索:
1 | GET /_search?q=mary |
在前一个例子中,我们搜索tweet或name字段中包含某个字符的结果。然而,这个语句返回的结果在三个不同的字段中包含”mary”:
用户的名字是“Mary”
“Mary”发的六个推文
针对“@mary”的一个推文
Elasticsearch是如何设法找到三个不同字段的结果的?
当你索引一个文档,Elasticsearch把所有字符串字段值连接起来放在一个大字符串中,它被索引为一个特殊的字段_all。例如,当索引这个文档:
1 | { |
2 | "tweet": "However did I manage before Elasticsearch?", |
3 | "date": "2014-09-14", |
4 | "name": "Mary Jones", |
5 | "user_id": 1 |
6 | } |
这好比我们增加了一个叫做_all的额外字段值:
“However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1”
若没有指定字段,查询字符串搜索(即q=xxx)使用_all字段搜索。