Elasticsearch是全文检索引擎的实现,之前的solr还没弄呢~又来个ES,太快。
Elastic 的底层是开源库 Lucene。但是,你没法直接用 Lucene,必须自己写代码去调用它的接口。
Elastic 是 Lucene 的封装,提供简单的RESTful API来隐藏Lucene的复杂性。
Elasticsearch和MongoDB/Redis/Memcache一样 本质上是一个非关系分布式型数据库,允许多台服务器协同工作。是一个接近实时的搜索平台,从索引这个文档到这个文档能够被搜索到只有一个轻微的延迟,企业应用的定位。
基本概念与关系型数据库的对比如下
Relational DBElasticsearch数据库(database)索引(indices)表(tables)types行(rows)documents字段(columns)fields Node 与 Cluster单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。
Cluster模式支持一主多从且扩容简易,只要cluster.name一致且在同一个网络中就能自动加入当前集群cuiyaonan2000@163.com
ShardsShards:代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改。
replicasreplicas代表索引副本,es可以设置多个索引的副本,副本的作用一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。
Recovery代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配,挂掉的节点重新启动时也会进行数据恢复。---这里跟mongodb的分片很相似
Index(DataBase)Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。------根据原始数据建立的,具有相关共性的索引集合。因为内容全是索引,故此就是一个索引数据库,对应关系型数据库中的Database。-----😒这里是ES的关键,其中还涉及到分词管理cuiyaonan2000@163.com
每个 Index (即数据库)的名字必须是小写。
下面的命令可以查看当前节点的所有 Index。
$ curl -X GET 'http://localhost:9200/_cat/indices?v'
Document(Row)
Index 里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。
Document 使用 JSON 格式表示,下面是一个例子。
同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。
{
"user": "cuiyaonan2000@163.com",
"title": "工程师",
"desc": "数据库管理"
}
Type(Group)
Document 可以分组,比如weather
这个 Index 里面,可以按城市分组(北京和上海),也可以按气候分组(晴天和雨天)。这种分组就叫做 Type,它是虚拟的逻辑分组,用来过滤 Document。
不同的 Type 应该有相似的结构(schema),举例来说,id
字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的一个区别。性质完全不同的数据(比如products
和logs
)应该存成两个 Index,而不是一个 Index 里面的两个 Type(虽然可以做到)。
下面的命令可以列出每个 Index 所包含的 Type。
根据规划,Elastic 6.x 版只允许每个 Index 包含一个 Type,7.x 版将会彻底移除 Type。
$ curl 'localhost:9200/_mapping?pretty=true'
Field(列)
因为Document的schema可以不一样,所以不是所有的document都有相同的列。
RestFul Options这里只是简单的一些操作,具体的请参照官网:Elasticsearch搜索API
全局操作 获取所有的索引http://192.168.137.100:9200/_cat/indices?v
新建和删除 Index
新建 Index,可以直接向 Elastic 服务器发出 PUT 请求。下面的例子是新建一个名叫weather
的 Index。index的名字必须是小写
$ curl -X PUT 'localhost:9200/weather'
服务器返回一个 JSON 对象,里面的acknowledged
字段表示操作成功。
{
"acknowledged":true,
"shards_acknowledged":true
}
然后,我们发出 DELETE 请求,删除这个 Index。
$ curl -X DELETE 'localhost:9200/weather'
中文分词设置
首先,安装中文分词插件。这里使用的是 ik,也可以考虑其他插件(比如 smartcn)。
中文分词其实只是一个选择,告诉ES我们该用哪个插件,具体分词操作不是我们这边做cuiyaonan2000@163.com
$ ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.5.1/elasticsearch-analysis-ik-5.5.1.zip
上面代码安装的是5.5.1版的插件,与 Elastic 5.5.1 配合使用。-----注意版本兼容性的问题
接着,重新启动 Elastic,就会自动加载这个新安装的插件。
然后,新建一个 Index,指定需要分词的字段。这一步根据数据结构而异,下面的命令只针对本文。基本上,凡是需要搜索的中文字段,都要单独设置一下。
$ curl -X PUT 'localhost:9200/accounts' -d '
{
"mappings": {
"person": {
"properties": {
"user": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
}'
上面代码中,首先新建一个名称为accounts
的 Index,里面有一个名称为person
的 Type。person
有三个字段。
- user
- title
- desc
这三个字段都是中文,而且类型都是文本(text),所以需要指定中文分词器,不能使用默认的英文分词器。-----同时说明了只有当我们的字段是类型是text的时候才会用到分词器cuiyaonan2000@163.com
Elastic 的分词器称为 analyzer。我们对每个字段指定分词器
analyzer
是字段文本的分词器,search_analyzer
是搜索词的分词器ik_max_word
分词器是插件ik
提供的,可以对文本进行最大数量的分词。
向指定的 /Index/Type 发送 PUT 请求,就可以在 Index 里面新增一条记录。比如,向/accounts/person
发送请求,就可以新增一条人员记录。
$ curl -X PUT 'localhost:9200/accounts/person/1' -d '
{
"user": "张三",
"title": "工程师",
"desc": "数据库管理"
}'
服务器返回的 JSON 对象,会给出 Index、Type、Id、Version 等信息。
{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_version":1,
"result":"created",
"_shards":{"total":2,"successful":1,"failed":0},
"created":true
}
如果你仔细看,会发现请求路径是/accounts/person/1
,最后的1
是该条记录的 Id。它不一定是数字,任意字符串(比如abc
)都可以。
新增记录的时候,也可以不指定 Id,这时要改成 POST 请求。
$ curl -X POST 'localhost:9200/accounts/person' -d '
{
"user": "李四",
"title": "工程师",
"desc": "系统管理"
}'
上面代码中,向/accounts/person
发出一个 POST 请求,添加一个记录。这时,服务器返回的 JSON 对象里面,_id
字段就是一个随机字符串。
{
"_index":"accounts",
"_type":"person",
"_id":"AV3qGfrC6jMbsbXb6k1p",
"_version":1,
"result":"created",
"_shards":{"total":2,"successful":1,"failed":0},
"created":true
}
注意:如果没有事先建立index ,ES也不会报错,同时会自动创建,所以要注意大小写。
查询Document向/Index/Type/Id
发出 GET 请求,就可以查看这条记录。
$ curl 'localhost:9200/accounts/person/1?pretty=true'
上面代码请求查看/accounts/person/1
这条记录,URL 的参数pretty=true
表示以易读的格式返回。
返回的数据中,found
字段表示查询成功,_source
字段返回原始记录。
{
"_index" : "accounts",
"_type" : "person",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理"
}
}
如果 Id 不正确,就查不到数据,found
字段就是false
。
$ curl 'localhost:9200/weather/beijing/abc?pretty=true'
{
"_index" : "accounts",
"_type" : "person",
"_id" : "abc",
"found" : false
}
删除Document
删除记录就是发出 DELETE 请求。
$ curl -X DELETE 'localhost:9200/accounts/person/1'
更新Document
更新记录就是使用 PUT 请求,重新发送一次数据。
$ curl -X PUT 'localhost:9200/accounts/person/1' -d '
{
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理,软件开发"
}'
//返回JSON
{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_version":2,
"result":"updated",
"_shards":{"total":2,"successful":1,"failed":0},
"created":false
}
可以看到,记录的 Id 没变,但是版本(version)从1
变成2
,操作类型(result)从created
变成updated
,created
字段变成false
,因为这次不是新建记录。
使用 GET 方法,直接请求/Index/Type/_search
,就会返回所有记录。
$ curl 'localhost:9200/accounts/person/_search'
{
"took":2,
"timed_out":false,
"_shards":{"total":5,"successful":5,"failed":0},
"hits":{
"total":2,
"max_score":1.0,
"hits":[
{
"_index":"accounts",
"_type":"person",
"_id":"AV3qGfrC6jMbsbXb6k1p",
"_score":1.0,
"_source": {
"user": "李四",
"title": "工程师",
"desc": "系统管理"
}
},
{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_score":1.0,
"_source": {
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理,软件开发"
}
}
]
}
}
total
:返回记录数,本例是2条。max_score
:最高的匹配程度,本例是1.0
。hits
:返回的记录组成的数组。- took:该次操作所耗费的毫秒数
- time_out: 该次操作是否超时
- _source: 表示匹配的程序,默认是按照这个字段降序排列。
$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query" : { "match" : { "desc" : "软件" }}
}'
上面代码使用 Match 查询,指定的匹配条件是desc
字段里面包含"软件"这个词。返回结果如下。
{
"took":3,
"timed_out":false,
"_shards":{"total":5,"successful":5,"failed":0},
"hits":{
"total":1,
"max_score":0.28582606,
"hits":[
{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_score":0.28582606,
"_source": {
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理,软件开发"
}
}
]
}
}
Elastic 默认一次返回10条结果,可以通过size
字段改变这个设置。
$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query" : { "match" : { "desc" : "管理" }},
"size": 1
}'
上面代码指定,每次只返回一条结果。
还可以通过from
字段,指定位移。--跟Mysql的Offset一样
$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query" : { "match" : { "desc" : "管理" }},
"from": 1,
"size": 1
}'
逻辑条件查询
如果有多个搜索关键字, Elastic 认为它们是or
关系。
$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query" : { "match" : { "desc" : "软件 系统" }}
}'
上面代码搜索的是软件 or 系统
。
如果要执行多个关键词的and
搜索,必须使用布尔查询。
$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query": {
"bool": {
"must": [
{ "match": { "desc": "软件" } },
{ "match": { "desc": "系统" } }
]
}
}
}'
搜索
搜索的格式是: //_search 其中 index和type是可选的,搜索的范围如下:
- http://localhost:9200/_search - 搜索所有索引和所有类型。---没有index和type
- http://localhost:9200/movies/_search - 在电影索引中搜索所有类型 ----有index
- http://localhost:9200/movies/movie/_search - 在电影索引中显式搜索电影类型的文档。 ----有index和type
那Elasticsearch的DSL格式如下:
curl -XPOST "http://localhost:9200/_search" -d'
{
"query": { ----query是关键字
"query_string": { ---query_string是es提供的查询类型
"query": "kill"
}
}
}'
如上所示Elasticsearch的格式首先必须有query,然后是个query_type(这是es提供的查询类型,或者叫对象,里面有很多参数,用于我们查询cuiyaonan2000@163.com)
如上query_string 只设置一个属性即 "query": "kill" 所以它会把所有记录中的任意字段包含kill的记录返回.
如果在明确一点,可以设置 query_string中的filed字段来限制记录中的哪个字段是否满足条件.
curl -XPOST "http://localhost:9200/_search" -d'
{
"query": {
"query_string": {
"query": "ford",
"fields": ["title"]
}
}
}'
过滤
这个过滤器的目的我在想可能是用于复杂的查询????
因为过滤器 = 查询+过滤 即 先查询出满足条件的记录,然后在根据一些条件进行过滤.
如下可以看到 过滤器(filtered) 其实是query另一个query_type,然后它包含了2个属性queyr和filter.
curl -XPOST "http://localhost:9200/_search" -d'
{
"query": {
"filtered": {
"query": {
"query_string": {
"query": "drama"
}
},
"filter": {
"term": { "year": 1962 }
}
}
}
}'
//如下的格式是将所有满足的记录都进行过滤
curl -XPOST "http://localhost:9200/_search" -d'
{
"query": {
"filtered": {
"query": {
"match_all": {
}
},
"filter": {
"term": { "year": 1962 }
}
}
}
}'
聚合
聚合与搜索我觉得挺好理解. 聚合就是针对我们的搜索内容进行计算cuiyaonan2000@163.com
聚合= 搜索/过滤 + 分析
那聚合内部是如何进行 数据分析的呢? 聚合的两个主要的概念,分别是 桶 和 指标
桶(Buckets) : 满足特定条件的文档的结果集合桶可以被嵌套在其他桶里面
像是一个员工属于男性桶或者女性桶,日期2014-10-28属于十月桶,也属于2014年桶
像是北京能放在中国桶裡,而中国桶能放在亚洲桶裡
Elasticsearch提供了很多种类型的桶,像是时间、最受欢迎的词、年龄区间、地理位置桶等等
指标(Metrics) : 对桶内的文档进行统计计算指标通常是简单的数学运算(像是min、max、avg、sum),而这些是通过当前桶中的文档的值来计算的,利用指标能让你计算像平均薪资、最高出售价格、95%的查询延迟这样的数据
aggs 聚合的模板aggs的官方标准格式为
"aggregations" : {
"" : { //聚合的名称
"" : { // 桶的名称,es默认提供了很多请注意cuiyaonan2000@163.com
//字段参数等
}
[,"meta" : { [] } ]?
[,"aggregations" : { []+ } ]?
}
}
另外需要注意的一些内容:
- 当query和aggs一起存在时,会先执行query的主查询,主查询query执行完后会搜出一批结果,而这些结果才会被拿去aggs拿去做聚合
- 一个aggs裡可以有很多个聚合,每个聚合彼此间都是独立的,因此可以一个聚合拿来统计数量、一个聚合拿来分析数据、一个聚合拿来计算标准差...,让一次搜索就可以把想要做的事情一次做完
- 如果有些情况不在意查询结果是什麽,而只在意aggs的结果,可以把size设为0,如此可以让返回的hits结果集是0,加快返回的速度-------值得query层的size设置成0哦cuiyaonan2000@163.com
- aggs可以嵌套在其他的aggs裡面,而嵌套的桶能作用的文档集范围,是外层的桶所输出的结果集
模拟一个索引只有1个字段,包含如下内容
-
{ "color": "red" }
-
{ "color": "green" }
-
{ "color": ["red", "blue"] }
GET 127.0.0.1/mytest/doc/_search
{
"query": {
"match_all": {}
},
"size": 0, //这里的0标识query查询的内容不用返回.我们只要聚合的返回,这样提升速度
"aggs": {
"my_name": {
"terms": {
"field": "color"
}
}
}
}
返回结果
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.0,
"hits": []
},
//这里是聚合的返回结果
"aggregations ": {
"my_name": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "blue",
"doc_count": 1
},
{
"key": "red",
"doc_count": 2 //为2的原因你猜测下
},
{
"key": "green",
"doc_count": 1
}
]
}
}
嵌套聚合
-
{ "color": "red", "price": 100 }
-
{ "color": "green", "price": 500 }
-
{ "color": ["red", "blue"], "price": 1000 }
如下my_name桶下首先根据color分组,然后在计算该组中的平均价格和最小价格cuiyaonan2000@163.com
{
"query":{
"match_all":{
}
},
"size":0,
"aggs":{
"my_name":{
"terms":{
"field":"color"
},
"aggs":{ //这里就是嵌套聚合, aggs是关键字
"my_avg_price":{ //桶的名称
"avg":{ //聚合分类
"field":"price"
}
},
"my_min_price":{
"min":{
"field":"price"
}
}
}
}
}
}
批量处理
批量处理不仅仅支持批量插入,删除,更新,创建索引等操作都支持.
- delete 删除操作,只需要写一个json即可
- create 创建操作,如果需要创建的文档已经存在,那么创建失败
- index 创建或替换操作,如果要创建的文档不存在则执行创建操作,如果已经存在则执行替换操作
- update 更新操作 执行文档的更新
POST http://path.to.your.cluster/_bulk
向指定的 /Index/Type 发送 PUT 请求,就可以在 Index 里面新增一条记录。比如,向/accounts/person发送请求,就可以新增一条人员记录。
第一行指定请求与索引和类型,可以选择的请求有"create",“index”,“delete”,“ubdate”,“_index"指定索引名,”_type"指定类型名,"_id"指定id
{ "index":{ "_index": "myIndex", "_type": "person" } }
{ "name":"john doe","age":25 }
{ "index":{ "_index": "myOtherIndex", "_type": "dog" } }
{ "name":"fido","breed":"chihuahua" }
除了索引名和类型外,还可以在批量插入请求中为每个文档提供id:
{ "index":{ "_id": "8a78dhkujg" } }
{ "name":"mary smith","age":32 }