作者:刘晓国
在今天的这篇文章中,我们来主要介绍一下如何使用 REST 接口来对 Elasticsearch 进行操作。为了完成这项工作,我们必须完成如下的步骤:
安装 Elasticsearch。请参阅文章 “如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch”。把 Elasticsearch 运行起来。
安装 Kibana。请参阅文章 “如何在 Linux,MacOS 及 Windows 上安装 Elastic 栈中的 Kibana”。把 Kibana 运行起来。
熟悉有关于 Elastic 栈的一些最基本的概念。请参阅文章 “Elasticsarch 中的一些重要概念: cluster, node, shards 及 replica”。这些概念对我们如下的练习有非常多的帮助。
有了上面最基本的一些安装及概念,我们就很容进行下面的讲解了。在如下所展示的所有的 scripts 可在地址 https://github.com/liu-xiao-guo/es-scripts-7.3 找到。
这个教程是一个3篇文章的教程,它涵盖了最基本的 Elasticsearch 的一些最基本的点:
开始使用 Elasticsearch (1): 了解如何创建索引,添加,删除,更新文档
开始使用 Elasticsearch (2):了解如何进行搜索
开始使用 Elasticsearch (3):了解如何进行分析数据: analyze 及 aggregate 数据
什么是JSON?
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。在 Elasticsearch 中,所以的数据都是以 JSON 的格式来进行表述的。这个和其它的有些数据库,比如 Solr,它支持更多格式的数据,比如 xml, csv 等。
我们来看一下一个简单的 JSON 格式的数据表达:
{ "name" : "Elastic", "location" : { "state" : "CA", "zipcode" : 94123 } }
这个看起来非常简单直接。如果大家熟悉 Javascript 的话,你会发现它和 Javascript 里的 Object 非常地相似。
什么是 REST 接口?
相信很多做过微服务架构的开发者来说,你们可能对 REST 接口再熟悉不过了。REST 即表述性状态传递(英文:Representational State Transfer,简称 REST)是 Roy Fielding 博士在2000年他的博士论文中提出来的一种软件架构风格。REST 是一种规范。即参数通过封装后进行传递,响应也是返回的一个封装对象。一个 REST 的接口就像如下的接口:
http://example.com/customers/1234
我们可以通过:
HTTP GET HTTP POST HTTP PUT HTTP DELETE HTTP PATCH
来对数据进行增加(Create),查询(Read),更新(Update)及删除(Delete)。也就是我们通常说是的 CRUD。
Elasticsearch 里的接口都是通过 REST 接口来实现的。我们在一下的章节里来重点介绍一下是如果使用 REST 接口来实现对数据的操作及查询的。
检查 Elasticsearch 及 Kibana 是否运行正常
我们首先在我们的浏览器中输入如下地址:http://localhost:9200。查看一下我们的输出:
如果你能看到如上的信息输出,表明我们的 Elasticsearch 是处于一个正常运行的状态。
同时,我们在浏览器中输入地址:http://localhost:5601。在浏览器中,我们查看输出的信息:
上面显示了 Kibana 的界面。由于 Kibana 的功能有很多。我们在今天的培训中就不一一介绍了。我们着重使用在上面显示的 “Dev Tools” 菜单里的功能。当我们点击它的时候,我们可以看到如下的界面。
当我们执行命令时,我们必须点击左边窗口里的那个绿色的播放按钮。命令所执行显示的结果将在右边展示。在接下的所有练习中,我们都将使用这样的操作来进行。
在使用 Kibana 之前,最后先熟悉一下一些快捷键的使用,你可以点击 help 链接得到更多的帮助:
查看 Elasticsearch 信息
就像我们之前在浏览器其中打入地址 http://localhost:9200 看到的效果一样,我们直接打入
GET /
我们就可以看到如下的信息:
在这里我们可以看到 Elasticsearch 的版本信息及我们正在使用的 Elasticsearch 的 Cluster 名称等信息。
在很多时候,我们也可以直接在 terminal 中打入相应的指令来达到同样的效果,不过在 Kibana 中更加直接:
我们把上面的命令拷贝为 cURL,然后再粘贴到 terminal 中,你就会看到:
同样地,我们也可以直接把如下的 terminal 命令:
curl -XGET "http://localhost:9200/"
直接拷贝并粘贴到 Dev Tools 的 Console 里去,你会发现,它变成为:
GET /
我们发现当我们打入一个命令时,Kibana 会帮我们自动地显示可以输入的选择项,它具有 autocomplete 的功能。这个对我们打入我们所需要的命令非常有用。我们有时候不需要记那么多。
创建一个索引及文档
我们接下来创建一个叫做 twitter 的索引(index),并插入一个文档(document)。我们知道在 RDMS 中,我们通常需要有专用的语句来生产相应的数据库,表格,让后才可以让我们输入相应的记录,但是针对 Elasticsearch 来说,这个是不必须的。我们在左边的窗口中输入:
PUT twitter/_doc/1 { "user": "GB", "uid": 1, "city": "Beijing", "province": "Beijing", "country": "China" }
我们可以看到在 Kibana 右边的窗口中有下面的输出:
请注意:在上面创建文档的过程中,我们并没有像其他 RDMS 系统一样,在输入文档之前需要定义各个字段的类型及长度等。当我们建立一个索引的第一个文档时,如果你没有创建它的 schema,那么 Elasticsearch 会根据所输入字段的数据进行猜测它的数据类型,比如上面的 user 被被认为是 text 类型,而 uid 将被猜测为整数类型。这种方式我们称之为 schema on write,也即当我们写入第一个文档时,Elasticsearch 会自动帮我们创建相应的 schema。在 Elasticsearch 的术语中,mapping 被称作为 Elasticsearch 的数据 schema。一旦一个索引的某个字段的类型被确定下来之后,那么后续导入的文档的这个字段的类型必须是和之前的是一致,否则写入将导致错误。schema on write 可能在某些时候不是我们想要的,那么在这种情况下,我们可以事先创建一个索引的 schema。你将在后面的文章中看到如何创建这个 schema。在最新的 Elasticsearch 设计中,也出现了一种叫做 schema on read 的设计。如果你对这个感兴趣的话,请参阅我的另外一篇文章 “Elasticsearch:Runtime fields 入门, Elastic 的 schema on read 实现 - 7.11 发布”。
Elasticsearch 的数据类型:
text:全文搜索字符串
keyword:用于精确字符串匹配和聚合
date 及 date_nanos:格式化为日期或数字日期的字符串
byte, short, integer, long:整数类型
boolean:布尔类型
float,double,half_float:浮点数类型
分级的类型:object 及 nested。你可以参考文章 “Elasticsearch: nested 对象”
有关 Elasticsearch 的数据类型,可以参考链接。
在上面,我们可以看出来我们已经成功地创建了一个叫做 twitter 的 index。通过这样的方法,我们可以自动创建一个 index。如果大家不喜欢自动创建一个 index,我们可以修改如下的一个设置:
PUT _cluster/settings { "persistent": { "action.auto_create_index": "false" } }
请注意:在绝大多数情况下,我们并不需要这么做,除非你知道你要做什么。详细设置请参阅链接。如果你想禁止自动创建索引,你必须配置action.auto_create_index以允许这些创建以下索引的组件:
PUT _cluster/settings { "persistent": { "action.auto_create_index": ".monitoring*,.watches,.triggered_watches,.watcher-history*,.ml*" } }
如果使用 Logstash 或 Beats,则应在上面的列表中添加其他索引名称。我们也可以在 elasticsearch.yml 里进行配置。
通常对一个通过上面方法写入到 Elasticsearch 的文档,在默认的情况下并不马上可以进行搜索。这是因为在 Elasticsearch 的设计中,有一个叫做 refresh 的操作。它可以使更改可见以进行搜索的操作。通常会有一个 refresh timer 来定时完成这个操作。这个周期为1秒。这也是我们通常所说的 Elasticsearch 可以实现秒级的搜索。当然这个 timer 的周期也可以在索引的设置中进行配置。如果我们想让我们的结果马上可以对搜索可见,我们可以用如下的方法:
PUT twitter/_doc/1?refresh=true { "user": "GB", "uid": 1, "city": "Beijing", "province": "Beijing", "country": "China" }
上面的方式可以强制使 Elasticsearch 进行 refresh 的操作,当然这个是有代价的。频繁的进行这种操作,可以使我们的 Elasticsearch 变得非常慢。另外一种方式是通过设置 refresh=wait_for。这样相当于一个同步的操作,它等待下一个 refresh 周期发生完后,才返回。这样可以确保我们在调用上面的接口后,马上可以搜索到我们刚才录入的文档:
PUT twitter/_doc/1?refresh=wait_for { "user": "GB", "uid": 1, "city": "Beijing", "province": "Beijing", "country": "China" }
如果你想对 refresh 有更多的了解,请参阅我的文章 “Elasticsearch 中的 refresh 和 flush 操作指南”。
它也创建了一个被叫做 _doc 的 type。自从 Elasticsearch 6.0 以后,一个 index 只能有一个 type。如果我们创建另外一个t ype 的话,系统会告诉我们是错误的。这里我们也会发现有一个版本(_version)信息,它显示的是4。如果这个 _id 为 1 的 document 之前没有被创建过的话,它会显示为 1。之后如果我们更改这个 document,它的版本会每次自动增加1。比如,我们输入:
POST twitter/_doc/1 { "user": "GB", "uid": 1, "city": "Shenzhen", "province": "Guangdong", "country": "China" }
我们在左边修改了我们的数据,在右边,我们可以看到版本信息增加到6。这是因为我们把左边的命令执行了两次。同时,我们也可以看出来,我们也把左边的数据进行了修改,我们也看到了成功被修改的返回信息。在上面我们可以看出来,我们每次执行那个 POST 或者 PUT 接口时,如果文档已经存在,那么相应的版本(_version)就会自动加
1,之前的版本抛弃。如果这个不是我们想要的,那么我们可以使 _create 端点接口来实现:
PUT twitter/_create/1 { "user": "GB", "uid": 1, "city": "Shenzhen", "province": "Guangdong", "country": "China" }
如果文档已经存在的话,我们会收到一个错误的信息:
上面的命令和如下的命令也是一样的效果:
PUT twitter/_doc/1?op_type=create { "user": "双榆树-张三", "message": "今儿天气不错啊,出去转转去", "uid": 2, "age": 20, "city": "北京", "province": "北京", "country": "中国", "address": "中国北京市海淀区", "location": { "lat": "39.970718", "lon": "116.325747" } }
在上面,我们在请求时带上 op_type。它可以有两种值:index 及 create。
我们必须指出的是,如果你是在 Linux 或 MacOS 机器上,我们也可以使用如下的命令行指令来达到同样的效果:
curl -XPUT 'http://localhost:9200/twitter/_doc/1?pretty' -H 'Content-Type: application/json' -d ' { "user": "GB", "uid": 1, "city": "Shenzhen", "province": "Guangdong", "country": "China" }'
本方法适用于一下所有的命令,如法炮制!
我们可以通过如下的命令来查看被修改的文档:
GET twitter/_doc/1
我们可以看到在右边显示了我们被修改的文档的结果。
如果我们只想得到这个文档的 _source 部分,我们可以使用如下的命令格式:
GET twitter/_doc/1/_source
在 Elasticsearch 7.0 之后,在 type 最终要被废除的情况下,我们建立使用如下的方法来获得 _source:
GET twitter/_source/1
自动 ID 生成
在上面,我特意为我们的文档分配了一个 ID。其实在实际的应用中,这个并不必要。相反,当我们分配一个 ID 时,在数据导入的时候会检查这个 ID 的文档是否存在,如果是已经存在,那么就更新到版本。如果不存在,就创建一个新的文档。如果我们不指定文档的 ID,转而让 Elasticsearch 自动帮我们生成一个 ID,这样的速度更快。在这种情况下,我们必须使用 POST,而不是 PUT,比如:
POST my_index/_doc { "content": "this is really cool" }
返回的结果:
{ "_index" : "my_index", "_type" : "_doc", "_id" : "egiY4nEBQTokU_uEEGZz", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1 }
从上面我们可以看出来,系统会为我们自动分配一个 ID 啊。
如果我们只对 source 的内容感兴趣的话,我们可以使用:
GET twitter/_doc/1/_source
这样我们可以直接得到 source 的信息:
{ "user" : "双榆树-张三", "message" : "今儿天气不错啊,出去转转去", "uid" : 2, "age" : 20, "city" : "北京", "province" : "北京", "country" : "中国", "address" : "中国北京市海淀区", "location" : { "lat" : "39.970718", "lon" : "116.325747" } }
我们也可以只获取 source 的部分字段:
GET twitter/_doc/1?_source=city,age,province
如果你想一次请求查找多个文档,我们可以使用 _mget 接口:
GET _mget { "docs": [ { "_index": "twitter", "_id": 1 }, { "_index": "twitter", "_id": 2 } ] }
我们也可以只获得部分字段:
GET _mget { "docs": [ { "_index": "twitter", "_id": 1, "_source":["age", "city"] }, { "_index": "twitter", "_id": 2, "_source":["province", "address"] } ] }
在这里,我们同时请求 id 为1和2的两个文档。
我们也可以简单地写为:
GET twitter/_doc/_mget { "ids": ["1", "2"] }
它和上面的做一个是一样的。使用一个命令同时获取 id 为1及2的文档。
在上面当我们写入数据时,我们有意识地把文档的 id 在命令中写了出来。如果我们不写这个 id 的话,ES 会帮我们自动生产一个 id:
POST twitter/_doc/
我可以看到右边的一个 id 像是一个随机的数值,同时我们可以看到它的一个版本信息为1。在实际的需要有大量导入数据的情况下,我们建议让系统自动帮我们生成一个 id,这样可以提高导入的速度。假如我们指定一个 id,通常 ES 会先查询这个 id 是否存在,然后在觉得是更新之前的文档还是创建一个新的文档。这里是分两步走。显然它比直接创建一个文档要慢!
我们也可以看出来系统所给出来的字段都是以下划线的形式给出来的,比如:_id, _shards, _index, _typed 等
修改一个文档
我们接下来看一下如何修改一个文档。在上面我们看到了可以使用 POST 的命令来修改改一个文档。通常我们使用 POST 来创建一个新的文档。在使用 POST 的时候,我们甚至不用去指定特定的 id,系统会帮我们自动生成。但是我们修改一个文档时,我们通常会使用 PUT 来进行操作,并且,我们需要指定一个特定的 id 来进行修改:
PUT twitter/_doc/1 { "user": "GB", "uid": 1, "city": "北京", "province": "北京", "country": "中国", "location":{ "lat":"29.084661", "lon":"111.335210" } }
如上面所示,我们使用 PUT 命令来对我们的 id 为1的文档进行修改。我们也可以使用我们上面学过的 GET 来进行查询:
GET twitter/_doc/1
{ "_index" : "twitter", "_type" : "_doc", "_id" : "1", "_version" : 8, "_seq_no" : 13, "_primary_term" : 1, "found" : true, "_source" : { "user" : "GB", "uid" : 1, "city" : "北京", "province" : "北京", "country" : "中国", "location" : { "lat" : "29.084661", "lon" : "111.335210" } } }
显然,我们的这个文档已经被成功修改了。
我们使用 PUT 的这个方法,每次修改一个文档时,我们需要把文档的每一项都要写出来。这对于有些情况来说,并不方便,我们可以使用如下的方法来进行修改:
POST twitter/_update/1 { "doc": { "city": "成都", "province": "四川" } }
我们可以使用如上的命令来修改我们的部分数据。同样我们可以使用 GET 来查询我们的修改是否成功:
从上面的显示中,我们可以看出来,我们的修改是成功的,虽然在我们修改时,我们只提供了部分的数据。
在关系数据库中,我们通常是对数据库进行搜索,让后才进行修改。在这种情况下,我们事先通常并不知道文档的 id。我们需要通过查询的方式来进行查询,让后进行修改。ES 也提供了相应的 REST 接口。
POST twitter/_update_by_query { "query": { "match": { "user": "GB" } }, "script": { "source": "ctx._source.city = params.city;ctx._source.province = params.province;ctx._source.country = params.country", "lang": "painless", "params": { "city": "上海", "province": "上海", "country": "中国" } } }
对于那些名字是中文字段的文档来说,在 painless 语言中,直接打入中文字段名字,并不能被认可。我们可以使用如下的方式来操作:
POST edd/_update_by_query { "query": { "match": { "姓名": "张彬" } }, "script": { "source": "ctx._source[\"签到状态\"] = params[\"签到状态\"]", "lang": "painless", "params" : { "签到状态":"已签到" } } }
在上面我们使用一个中括号并 escape 引号的方式来操作。有关 Painless 的编程,你可以参阅文章 “Elasticsearch: Painless script 编程”。
我们可以通过上面的方法搜寻 user 为 GB 的用户,并且把它的数据项修改为:
"city" : "上海", "province": "上海", "country": "中国"
我们也可以通过 update 接口,使用 script 的方法来进行修改。这个方法也是需要知道文档的 id:
POST twitter/_update/1 { "script" : { "source": "ctx._source.city=params.city", "lang": "painless", "params": { "city": "长沙" } } }
和前面的方法一下,我们可以使用 GET 来查询,我们的结果是否已经改变:
{ "_index" : "twitter", "_type" : "_doc", "_id" : "1", "_version" : 18, "_seq_no" : 39, "_primary_term" : 1, "found" : true, "_source" : { "uid" : 1, "country" : "中国", "province" : "上海", "city" : "长沙", "location" : { "lon" : "111.335210", "lat" : "29.084661" }, "user" : "GB" } }
如果你涉及到多个客户端同时更新一个索引的情况,你需要阅读文章 “深刻理解文档中的 verision 及 乐观并发控制”。
我们甚至可以使用 _update 接口使用 ctx['_op'] 来达到删除一个文档的目的,比如:
POST twitter/_update/1 { "script": { "source": """ if(ctx._source.uid == 1) { ctx.op = 'delete' } else { ctx.op = "none" } """ } }
当检测文档的 uid 是否为 1,如果为 1 的话,那么该文档将被删除,否则将不做任何事情。
UPSERT 一个文档
仅在文档事先存在的情况下,我们在前面的代码中看到的部分更新才有效。 如果具有给定 ID 的文档不存在,Elasticsearch 将返回一个错误,指出该文档丢失。 让我们了解如何使用更新 API 进行 upsert 操作。 术语 “upsert” 宽松地表示更新或插入,即更新文档(如果存在),否则,插入新文档。
doc_as_upsert 参数检查具有给定ID的文档是否已经存在,并将提供的 doc 与现有文档合并。 如果不存在具有给定 ID 的文档,则会插入具有给定文档内容的新文档。
下面的示例使用 doc_as_upsert 合并到 ID 为3的文档中,或者如果不存在则插入一个新文档:
POST /catalog/_update/3 { "doc": { "author": "Albert Paro", "title": "Elasticsearch 5.0 Cookbook", "description": "Elasticsearch 5.0 Cookbook Third Edition", "price": "54.99" }, "doc_as_upsert": true }
检查一个文档是否存在
有时候我们想知道一个文档是否存在,我们可以使用如下的方法:
HEAD twitter/_doc/1
这个 HEAD 接口可以很方便地告诉我们在 twitter 的索引里是否有一 id 为1的文档:
上面的返回值表面 id 为1的文档时存在的。
删除一个文档
如果我们想删除一个文档的话,我们可以使用如下的命令:
DELETE twitter/_doc/1
在上面的命令中,我们删除了 id 为1的文档。
在关系数据库中,我们通常是对数据库进行搜索,让后才进行删除。在这种情况下,我们事先通常并不知道文档的 id。我们需要通过查询的方式来进行查询,让后进行删除。ES 也提供了相应的 REST 接口。
POST twitter/_delete_by_query { "query": { "match": { "city": "上海" } } }
这样我们就把所有的 city 是上海的文档都删除了。
检查一个索引是否存在
我们可以使用如下的命令来检查一个索引是否存在:
HEAD twitter
如果 twitter 索引存在,那么上面的命令会返回:
200 - OK
否则就会返回:
{"statusCode":404,"error":"Not Found","message":"404 - Not Found"}
删除一个索引
删除一个索引 是非常直接的。我们可以直接使用如下的命令来进行删除:
DELETE twitter
当我们执行完这一条语句后,所有的在 twitter 中的所有的文档都将被删除。
批处理命令
上面我们已经了解了如何使用 REST 接口来创建一个 index,并为之创建,读取,修改,删除文档(CRUD)。因为每一次操作都是一个 REST 请求,对于大量的数据进行操作的话,这个显得比较慢。ES 创建一个批量处理的命令给我们使用。这样我们在一次的 REST 请求中,我们就可以完成很多的操作。这无疑是一个非常大的好处。下面,我们来介绍一下这个 _bulk 命令。
我们使用如下的命令来进行 bulk 操作:
POST _bulk { "index" : { "_index" : "twitter", "_id": 1} } {"user":"双榆树-张三","message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"北京","province":"北京","country":"中国","address":"中国北京市海淀区","location":{"lat":"39.970718","lon":"116.325747"}} { "index" : { "_index" : "twitter", "_id": 2 }} {"user":"东城区-老刘","message":"出发,下一站云南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区台基厂三条3号","location":{"lat":"39.904313","lon":"116.412754"}} { "index" : { "_index" : "twitter", "_id": 3} } {"user":"东城区-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区","location":{"lat":"39.893801","lon":"116.408986"}} { "index" : { "_index" : "twitter", "_id": 4} } {"user":"朝阳区-老贾","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区建国门","location":{"lat":"39.718256","lon":"116.367910"}} { "index" : { "_index" : "twitter", "_id": 5} } {"user":"朝阳区-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区国贸","location":{"lat":"39.918256","lon":"116.467910"}} { "index" : { "_index" : "twitter", "_id": 6} } {"user":"虹桥-老吴","message":"好友来了都今天我生日,好友来了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中国","address":"中国上海市闵行区","location":{"lat":"31.175927","lon":"121.383328"}}
在上面的命令中,我们使用了 bulk 指令来完成我们的操作。在输入命令时,我们需要特别的注意:千万不要添加除了换行以外的空格,否则会导致错误。在上面我们使用的 index 用来创建一个文档。为了说明问题的方便,我们在每一个文档里,特别指定了每个文档的 id。当执行完我们的批处理 bulk 命令后,我们可以看到:
显然,我们的创建时成功的。因为我运行了两遍的原因,所以你看到的是 version 为2的返回结果。bulk 指令是高效的,因为一个请求就可以处理很多个操作。在实际的使用中,我们必须注意的是:一个好的起点是批量处理1000到5,000个文档,总有效负载在 5MB 到 15MB 之间。如果我们的 payload 过大,那么可能会造成请求的失败。如果你想更进一步探讨的话,你可以使用文件 accounts.json 来做实验。更多是有数据可以在地址 加载示例数据 | Kibana 用户手册 | Elastic 进行下载。
如果你想查询到所有的输入的文档,我们可以使用如下的命令来进行查询:
POST twitter/_search
这是一个查询的命令,在以后的章节中,我们将再详细介绍。通过上面的指令,我们可以看到所有的已经输入的文档。
上面的结果显示,我们已经有6条生产的文档记录已经生产了。
我们可以通过使用 _count 命令来查询有多少条数据:
GET twitter/_count
上面我们已经使用了 index 来创建6条文档记录。我也可以尝试其它的命令,比如 create:
POST _bulk { "create" : { "_index" : "twitter", "_id": 1} } {"user":"双榆树-张三","message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"北京","province":"北京","country":"中国","address":"中国北京市海淀区","location":{"lat":"39.970718","lon":"116.325747"}} { "index" : { "_index" : "twitter", "_id": 2 }} {"user":"东城区-老刘","message":"出发,下一站云南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区台基厂三条3号","location":{"lat":"39.904313","lon":"116.412754"}} { "index" : { "_index" : "twitter", "_id": 3} } {"user":"东城区-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区","location":{"lat":"39.893801","lon":"116.408986"}} { "index" : { "_index" : "twitter", "_id": 4} } {"user":"朝阳区-老贾","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区建国门","location":{"lat":"39.718256","lon":"116.367910"}} { "index" : { "_index" : "twitter", "_id": 5} } {"user":"朝阳区-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区国贸","location":{"lat":"39.918256","lon":"116.467910"}} { "index" : { "_index" : "twitter", "_id": 6} } {"user":"虹桥-老吴","message":"好友来了都今天我生日,好友来了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中国","address":"中国上海市闵行区","location":{"lat":"31.175927","lon":"121.383328"}}
在上面,我们的第一个记录里,我们使用了 create 来创建第一个 id 为1的记录。因为之前,我们已经创建过了,所以我们可以看到如下的信息:
从上面的信息,我们可以看出来 index 和 create 的区别。index 总是可以成功,它可以覆盖之前的已经创建的文档,但是 create 则不行,如果已经有以那个 id 为名义的文档,就不会成功。
我们可以使用 delete 来删除一个已经创建好的文档:
POST _bulk { "delete" : { "_index" : "twitter", "_id": 1 }}
我们可以看到 id 为1的文档已经被删除了。我可以通过如下的命令来查看一下:
显然,我们已经把 id 为1的文档已经成功删除了。
我们也可以是使用 update 来进行更新一个文档。
POST _bulk { "update" : { "_index" : "twitter", "_id": 2 }} {"doc": { "city": "长沙"}}
运行的结果如下:
同样,我们可以使用如下的方法来查看我们修改的结果:
我们可以清楚地看到我们已经成功地把城市 city 修改为“长沙”。
注意:通过 bulk API 为数据编制索引时,你不应在集群上进行任何查询/搜索。 这样做可能会导致严重的性能问题。
如果你对脚本编程比较熟悉的话,你可能更希望通过脚本的方法来把大量的数据通过脚本的方式来导入:
$ curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary @request_example.json
这里的 request_example.json 就是我们的 JSON 数据文件。我们可以做如下的实验:
下载测试数据:
wget https://github.com/liu-xiao-guo/elasticsearch-bulk-api-data/blob/master/es.json
然后在命令行中打入如下的命令:
curl -u elastic:123456 -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary @es.json
这里的 “elastic:123456” 是我们的 Elasticsearch 的用户名及密码,如果我们没有为我们的 Elasticsearch 设置安全,那么可以把 “-u elastic:123456” 整个去掉。正对配置有 https 的 Elasticsearch 服务器,我们可以使用如下格式的命令来进行操作:
curl --cacert /home/elastic/ca.crt -u elastic:123456 -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary @es.json
在上面, 我们使用 --cacert /home/elastic/ca.crt 来定义证书的地址。
等我们运行完上面的指令后,我们可以在 Kibana 中查看到我们的叫做 “bank_account” 的索引。
Open/close Index
Elasticsearch 支持索引的在线/离线模式。 使用脱机模式时,在群集上几乎没有任何开销地维护数据。 关闭索引后,将阻止读/写操作。 当你希望索引重新联机时,只需打开它即可。 但是,关闭索引会占用大量磁盘空间。 你可以通过将 cluster.indices.close.enable 的默认值从 true 更改为 false 来禁用关闭索引功能,以避免发生意外。
一旦 twitter 索引被关闭了,那么我们再访问时会出现如下的错误:
我们可以通过 _open 接口来重新打开这个 index:
Freeze/unfreeze index
冻结索引(freeze index)在群集上几乎没有开销(除了将其元数据保留在内存中),并且是只读的。 只读索引被阻止进行写操作,例如 docs-index 或 force merge。 请参阅冻结索引和取消冻结索引。
冻结索引受到限制,以限制每个节点的内存消耗。 每个节点的并发加载的冻结索引数受 search_throttled 线程池中的线程数限制,默认情况下为1。 默认情况下,即使已明确命名冻结索引,也不会针对冻结索引执行搜索请求。 这是为了防止由于误将冻结的索引作为目标而导致的意外减速。 如果要包含冻结索引做搜索,必须使用查询参数 ignore_throttled = false 来执行搜索请求。
我们可以使用如下的命令来对 twitter 索引来冻结:
POST twitter/_freeze
在执行上面的命令后,我们再对 twitter 进行搜索:
我们搜索不到任何的结果。按照我们上面所说的,我们必须加上 ignore_throttled=false 参数来进行搜索:
显然对于一个 frozen 的索引来说,我们是可以对它进行搜索的。我们可以通过如下的命令来对这个已经冻结的索引来进行解冻:
POST twitter/_unfreeze
一旦我们的索引被成功解冻,那么它就可以像我们正常的索引来进行操作了,而不用添加参数 ignore_throttled=false 来进行访问。
总结
在这篇文章中,我们详细地介绍了如果在 Elasticserch 中创建我们的索引,文档,并对他们进行更改,删除,查询的操作。希望对大家有所帮助。在接下来的文章里,我们将重点介绍如何对 Elasticsearch 里的 index 进行搜索和分析。
如果你想了解更多关于 Elastic Stack 相关的知识,请参阅我们的官方网站:Elastic Stack and Product Documentation | Elastic
下一步
接下来,我们可以学习教程:
观看原视频点击>>http:aaaa
开始使用Elasticsearch (1)