很多东西在工作中一直使用,但是从来没有认真总结过,遇到问题了就上网翻翻文档,工作中的问题是解决了,但是回过头去看,又好像什么都不懂,只是对经常使用的知识比较熟悉而已。这也是这篇文章产生的原因,希望通过这种方式,让自己的知识更加系统化。好了,下面就来说说ElasticSearch,这篇文章偏入门些,如果您已经对ElasticSearch非常熟悉了,当然也可以再浏览下,如果你还从来没接触过,希望这篇文章可以帮助到你。
我们下面的讲解与实例都是基于5.2版本的,由于ElasticSearch之前的版本跟目前这个版本有一定的差别,所以如果当你对照例子发现无法运行时,那么最可能的原因就是版本的问题了。
Elasticsearch是一个基于Lucene的开源搜索引擎,而Lucene本身是非常复杂的,要想直接使用Lucene并不是太容易。Elasticsearch的出现就是希望通过简单的RESTful API
来隐藏Lucene的复杂性,从而让全文搜索变得简单。
ES的安装
ES的安装是非常简单的,唯一需要我们配置的就是java环境,5.2的版本需要java8的支持,所以首先确保本地已经安装了java8或以上版本。接下来下载对应的二进制包,如果是mac或linux系统可以直接通过curl来下载
curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.2.tar.gz
对下载好的压缩包进行解压处理
tar -xvf elasticsearch-5.2.2.tar.gz
然后进入到解压后的elasticsearch-5.2.2/bin
目录中,直接运行elasticsearch
bin ./elasticsearch
之后,新打开一个终端窗口,执行如下的命令:
curl 'http://localhost:9200/?pretty'
如果启动成功,我们会看到类似下面的响应信息:
{
"name" : "EF-dAP-",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "4KsT9m6iRfqUfafnsklDcw",
"version" : {
"number" : "5.2.2",
"build_hash" : "f9d9b74",
"build_date" : "2017-02-24T17:26:45.835Z",
"build_snapshot" : false,
"lucene_version" : "6.4.1"
},
"tagline" : "You Know, for Search"
}
ES的几个概念
在ES中,数据都是按照JSON格式存储的,比如下面的例子
"email": "[email protected]",
"first_name": "li",
"last_name": "si",
"info": {
"age": 25,
"interests": [ "dolphins", "whales" ]
},
"join_date": "2017/04/06"
它描述了一个user对象,同时,在ES中它也代表了一个文档。ES在对这个文档进行存储的同时,还会对文档中的内容进行倒排索引,以便于后续的查找、过滤、排序等操作。
在关系型数据库中,我们熟悉的是database
、table
、row
、column
等概念,同样,在ES中也存在着类似的定义。
在ES中,索引(index)指的是具有相同属性的文档的集合,每个索引(index)包含多个类型(type),每个类型又包含多个文档(document),每个文档包含了多个字段(field)。如果跟关系型数据库的结构对照的话,大致可以如下表示:
Elasticsearch | 关系型数据库 |
---|---|
Index | Database |
Type | Table |
Document | Row |
Field | Column |
在ES中,索引是一个非常重要的概念,不同的场景下代表了不同的含义。这里有必要对在ES中使用的索引一词做一下澄清。
传统的关系型数据库中,索引代表对数据库中的一列或多列的值进行排序的一种存储结构,利用索引可以快速获得所需的内容。
而在ES中,索引作为名词来讲表示的是一个存储了相关文档的集合,可以对比于关系型数据库中的database
。当我们在ES中保存数据时,也称为对其进行索引处理,这里按动词理解,可以对比于关系型数据库中的insert
操作。
Elasticsearch能够对全文本进行快速的搜索,得益于其实用的一种数据结构:
倒排索引(Inverted Index)
。在倒排索引中,包含一个在所有文档中出现的字词的列表,其中,每一个字词又对应一个列表,这个列表的内容就是所有包含这个字词的文档。当搜索时,首先会对我们搜索的内容进行分词处理,然后检查每个分好的词对应哪些文档,最后汇总给我们结果。
ES常用操作
ES提供了非常丰富和强大的REST API
,通过这些API我们可以检查集群、节点和索引的健康情况,并对它们进行管理,可以在索引上对数据进行增删改查,更可以执行类似分页、排序、过滤、聚合等这样的高级查询操作。
对集群、节点和索引的操作
- 查看集群健康状态
curl -XGET 'localhost:9200/_cat/health?v&pretty'
在返回的结果中,包含一个status
字段,它的取值有三种,分别是green
、yellow
和red
,green
意味着一切正常,yellow
意味着数据是可用的,但是一些备份数据还没准备好,最后red
意味某些数据是不可用的,尽管如此,但部分功能还是可用的,只不过你需要尽快去处理。
- 查看集群下的节点信息
curl -XGET 'localhost:9200/_cat/nodes?v&pretty'
- 查看当前集群都有哪些索引
curl -XGET 'localhost:9200/_cat/indices?v&pretty'
- 新建一个名为customer的索引
curl -XPUT 'localhost:9200/customer?pretty&pretty'
- 删除名为customer的索引
curl -XDELETE 'localhost:9200/customer?pretty&pretty'
- 创建别名
curl -XPUT 'localhost:9200/xx_questions_v1/_alias/xx_questions'
- 通过别名查询所指向的索引
curl -XGET 'localhost:9200/_alias/xx_questions?pretty'
// or
curl -XGET 'localhost:9200/_alias/xx_questions*?pretty'
- 查询指向该索引下的所有别名
curl -XGET 'localhost:9200/xx_questions_v1/_alias?pretty'
- 删除别名
curl -XDELETE 'localhost:9200/xx_questions_v1/_alias/xx_questions'
- 删除别名的同时添加别名到新的索引
curl -XPOST 'http://localhost:9200/_aliases' -d '
{
"actions" : [
{ "remove" : { "index" : "dm_v1", "alias" : "dm" } },
{ "add" : { "index" : "dm_v2", "alias" : "dm" } }
]
}'
- 修改索引的设置
curl -XPUT 'http://localhost:9200/task_es/_settings' -H 'Content-Type:appliation' -d '{
"index": {
"refresh_interval": "30s",
"number_of_replicas": 1
}
}'
- 创建mapping
curl -XPOST 'http://localhost:9200/{index}/_mapping?pretty=true' -H 'Content-Type:application/json' -d '{
// mapping
}'
索引上的增删改查
首先,在customer索引上新增一个文档
curl -XPUT 'localhost:9200/customer/external/1?pretty&pretty' -H 'Content-Type: application/json' -d'
{
"first_name": "zhang", "last_name":"si"
}
'
上面的语句执行完毕后,会在customer索引中的external类型中新加入一个ID为1的文档。我们可以通过ID来访问这个新加入的文档:
curl -XGET 'localhost:9200/customer/external/1?pretty&pretty'
它的结果应该像下面这样,除了一个found
字段,就没啥特别的了
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : { "first_name": "zhang", "last_name": "si"}
}
这里要说明的是,Elasticsearch中对数据进行操作后,并不是立即就能够搜索的,这中间会有大概1秒钟的延迟。
现在,假如我们想将这个ID为1的文档替换成其他的文档,比如换成名字为lisi的文档,则只需要再执行一次上面的put操作即可,ID还是指定为1:
curl -XPUT 'localhost:9200/customer/external/1?pretty&pretty' -H 'Content-Type: application/json' -d'
{
"first_name": "li", "last_name":"si"
}
'
再次获取ID为1的文档,我们发现名称已经变成了lisi。假如说我们在执行替换的时候指定的ID不是1,比如是2,会怎样呢?
curl -XPUT 'localhost:9200/customer/external/2?pretty&pretty' -H 'Content-Type: application/json' -d'
{
"first_name": "wang", "last_name": "wu"
}
'
这句话执行的结果就是索引了一个新的文档,新文档的ID是2。
可以看到,ES跟传统关系型数据库是不同的,在传统关系型数据库中,如果库中已经有一个ID是1的数据(这里ID作为主键)则再执行插入为1的操作会报错。而在ES中,通过PUT可以来进行create
和update
这两种操作,我们可以这样理解:当ES中已经存在特定的ID时,会执行更新操作,当ES中没有这个ID时会执行插入操作。
在ES内部,其实并不是真正的去更新文档,而是先删除旧的文档,再重新索引这个新的文档。所以,也就不难理解为什么上面的两个PUT操作会有不同的效果了。
除了索引文档的时候可以指定ID外,也可以不指定,如果不指定ID,ES会生成一个随机的ID,同时,必须使用POST方法来索引一个文档:
curl -XPOST 'localhost:9200/customer/external?pretty&pretty' -H 'Content-Type: application/json' -d'
{
"first_name": "zhao", "last_name": "liu"
}
'
对于更新操作,有的时候我们并不想更新整个文档,而只是想更新文档中的某个字段,怎么操作呢?我们还以上面为例,假如只想改变first_name字段,名字改成zhang lisi:
curl -XPOST 'localhost:9200/customer/external/1/_update?pretty&pretty' -H 'Content-Type: application/json' -d'
{
"doc": { "first_name": "zhang" }
}
'
与上面替换整个文档不同的是,我们这里使用的是POST操作,同时在url中指定了是_update
,并且,将要更新的内容嵌套在了doc
字段中。通过这个操作,我们不但可以更新已有的字段,也可以新增字段,比如,下面的操作会新增一个age字段:
curl -XPOST 'localhost:9200/customer/external/1/_update?pretty&pretty' -H 'Content-Type: application/json' -d'
{
"doc": { "first_name": "zhang", "age": 25 }
}
'
根据RESTful API
规范,我们不难推断出来,删除一个文档只要使用DELETE
句可以了:
curl -XDELETE 'localhost:9200/customer/external/2?pretty&pretty'
我们上面的操作都是基于单个文档的,ES同样提供了批量操作的接口。使用批量操作也很简单,首先在URL上通过_bulk
告诉ES我们使用的是批量操作,其次,对于每个数据,我们还要告诉ES是什么类型的操作,比如下面的例子:
curl -XPOST 'localhost:9200/customer/external/_bulk?pretty&pretty' -H 'Content-Type: application/json' -d'
{"index":{"_id":"1"}}
{"first_name": "zhang", "last_name": "san"}
{"index":{"_id":"2"}}
{"first_name": "li", "last_name": "si"}
'
{"index":{"_id":"1"}}
告诉ES是要新索引一个文档,文档的ID是1,紧跟着的{"first_name": "zhang", "last_name": "san"}
是这个ID为1的文档的内容,同理,我们也可以在一次批处理中同时包含更新和删除操作:
curl -XPOST 'localhost:9200/customer/external/_bulk?pretty&pretty' -H 'Content-Type: application/json' -d'
{"update":{"_id":"1"}}
{"doc": { "last_name": "san si wu" } }
{"delete":{"_id":"2"}}
'
上面的操作会首先更新ID为1的文档的last_name字段,然后删除ID为2的文档。
在入门(1)中,主要就是介绍一下ES的基本的概念和一些基本的操作,在入门(2)中会继续介绍ES中的高级查询。