学习Elasticsearch
目录
一、Elasticsearch定义
处理实时搜索、搜索引擎库
Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。
主要特点
1.实时分析
2.分布式实时文件存储,并将每一个字段都编入索引
3.文档导向,所有的对象全部是文档
4.高可用性,易扩展,支持集群(Cluster)、分片和复制(Shards 和 Replicas)。
5.接口友好,支持 JSON
二、核心概念
1.index索引
一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。可类比mysql中的数据库。
2.type类型
在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。通常,会为具有一组共同字段的文档定义一个类型。比如说,我们假设你运营一个博客平台并且将你所有的数据存储到一个索引中。在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类型,当然,也可以为评论数据定义另一个类型。 可类比mysql中的表。
3.Filed字段
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识 。
4.映射mapping
mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等,这些都是映射里面可以设置的,其它就是处理es里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好。相当于mysql中的创建表的过程,设置主键外键等等。
5.document文档
一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以JSON(Javascript Object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。在一个index/type里面,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被索引/赋予一个索引的type。 插入索引库以文档为单位,类比与数据库中的一行数据
6.集群cluster
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。一个集群由 一个唯一的名字标识,这个名字默认就是“elasticsearch”。这个名字是重要的,因为一个节点只能通过指定某个集 群的名字,来加入这个集群。
7.节点node
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一 个节点也是由一个名字来标识的,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的 时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对 应于Elasticsearch集群中的哪些节点。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫 做“elasticsearch”的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此, 它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点, 这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
8.分片和复制 shards&replicas
一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。分片很重要,主要有两方面的原因: 1)允许你水平分割/扩展你的内容容量。 2)允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量。
至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的。
在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
复制之所以重要,有两个主要原因: 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行。总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变分片的数量。
三、ES基础操作
1.IK分词器
分词:即把一段中文或者别的内容划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,是因为数据库中或者索引库中的数据也会进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如 “我爱大数据” 会被分为"我","爱","大", "数","据",这显然是不符合要求的,所以我们需要安装中文分词器 ik 来解决这个问题。
IK提供了两个分词算法:ik_smart 和 ik_max_word ,其中 ik_smart 为最少切分,ik_max_word 为 最细粒度划分!
尝试用过 ik_smart 最少切分,发起一个 GET 请求
GET _analyze
{
"analyzer":"ik_smart",
"text": "梦想家"
}
再用相同的文字测试一次ik_max_word 最细粒度划分:
GET _analyze
{
"analyzer":"ik_max_word",
"text": "梦想家"
}
ik_max_word 是细粒度分词,会穷尽一个语句中所有分词可能 ik_smart 是粗粒度分词,优先匹配最长词,不会有重复的数据。细粒度分词穷尽词库的可能,那词库是哪里的?一定是存在有类似"字典"这样的东西,不相信的话, 我们再用细粒度分词ik_max_word来做个测试
GET _analyze
{
"analyzer":"ik_smart",
"text": "超级喜欢狂神说"
}
可以发现此时狂神说几个字被拆开了,那如果我们想让系统识别“狂神说”是一个词,就需要我们编辑自定义词库。
步骤同样非常简单:
(1) 进入elasticsearch/plugins/ik/config目录
(2) 新建一个my.dic文件,编辑内容:
狂神说
(3)修改IKAnalyzer.cfg.xml(在 ik/config 目录下)
<properties>
<comment>IK Analyzer 扩展配置</comment> <!-- 用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">my.dic</entry> <!-- 用户可以在这里配置自己的扩展停止词字典 -->
<entry key="ext_stopwords"></entry>
</properties>
(4)修改完配置重新启动 elasticsearch,再次测试!
此时可以发现,狂神说已经变成了一个词了~
2.Rest风格
什么是 Rest 风格呢?
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
创建索引
PUT 索引名/类型名/文档id {请求id}
// 命令解释
// PUT 创建命令 test1 索引 type1 类型 1 id
PUT test1/type1/1
{
"name":"大数据梦想家",
"age":21
}
返回结果 (是以REST ful 风格返回的 ):
那么 name 这个字段用不用指定类型呢。毕竟我们关系型数据库 是需要指定类型的
elasticsearch 常见的字段类型如下:
1.字符串类型
2.数值类型
long, integer, short, byte, double, float, half_float, scaled_float
3.日期类型
4.布尔值类型
5.二进制类型
2.1Put新增
新建一个索引并指定字段类型
PUT test2
{
"mappings": {
"properties": {
"name":{
"type":"text"
},
"age":{
"type":"long"
},
"birthday":{
"type":"date"
}
}
}
}
输出如下,说明创建成功了
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "test2"
2.2Get查看
尝试使用一下 GET 命令,请求具体的信息!
Get /test2
可以发现通过GET请求,我们能够详细获取到该索引下具体的信息,其中包含字段类型。那上面示例中字段类型是我自己定义的,那么我们不定义类型会是什么情况呢?
我们首先发起一个PUT请求,创建一个新的索引 test3,并添加一条数据
PUT test3/_doc/1
{
"name":"大数据梦想家",
"age":21,
"birthday":"2000-02-06"
}
然后通过GET请求,可以发现非常的智能。但是如果我们的文档字段类型没有指定,那么es就会给我们默认配置的字段类型!
2.3Update修改
如果我们想要修改文档里的字段信息呢?我们可以选择 UPDATE 也可以 选择 PUT进行覆盖
例如我可以像下图中的例子,将之前test3索引中的1号文档中的 name 字段修改后,重复提交,发现更新成功,但是注意 version 版本号已经变成了2
但是注意这种方法有弊端,如果我们在PUT的过程中,遗漏了字段,那么数据就会被新数据覆盖!所以,修改数据不建议使用PUT覆盖的方式!我们使用 POST 命令,在 id 后面跟 _update ,要修改的内容放到 doc 文档(属性)中即可。
Post test3/_doc/1/_update
{“doc”:{ “name”:“法外狂徒张三”}}
可以发现此时更新之后的version变成了3。所以,一旦索引被创建了之后,所有的修改都可以通过版本号看到变化。
2.4Delete删除
那么怎么删除一条索引呢(库)呢?我们需要使用到DELETE命令
2.5其他命令
通过GET _cat/health来获取集群的一个健康状态(待验证)
验证:通过GET _cluster/health或者_cluster/health?pretty来获取集群的一个健康状态 pretty美化输出的关键字
通过命令GET _cat/indices?v,我们可以获取到当前索引的很多信息,返回值包括所有索引的状态健康情况,分片,数据储存大小等等(待验证)
3.关于文档的基本操作
3.13.3.创建索引
首先先重新创建一个新的索引,并添加一些数据
PUT alice/user/1
{
"name":"爱丽丝",
"age":21,
"desc":"在最美的年华,做最好的自己!",
"tags":["技术宅","温暖","思维活跃"]
}
PUT alice/user/2
{
"name":"张三",
"age":23,
"desc":"法外狂徒",
"tags":["渣男","交友"]
}
PUT alice/user/3
{
"name":"路人甲",
"age":24,
"desc":"不可描述",
"tags":["靓仔","网游"]
}
3.2简单查询
通过 GET 命令,我们可以搜索到指定 id 的文档信息
GET alice/user/1
这是简单的搜索,我们来看一下 es 如何做条件查询
3.3条件查询
条件查询_search?q=
我们可以通过如下命令,来进行条件查询
GET alice/user/_search?q=name:张三
我们看一下结果 返回并不是 数据本身,是给我们了一个 hits ,还有 _score得分,就是根据算法算出和查询条件匹配度高的分就越高。我们在以某度为例的搜索引擎上进行搜索也是一样的道理,权重越高网站的位置就越靠前!但我们一般使用不会直接加条件去查询,更多的会用到下面要介绍到的复杂操作搜索。
复杂操作搜索 select( 排序,分页,高亮,模糊查询,精准查询!)
为了方便测试,往Alice索引下添加了2个文档
PUT /alice/user/4
{
"name":"爱丽丝学Java",
"age":25,
"desc":"技术成就自我!",
"tags":["思维敏捷","喜欢学习"]
}
PUT /alice/user/5
{
"name":"爱丽丝学Python",
"age":26,
"desc":"人生苦短,我用Python!",
"tags":["好学","勤奋刻苦"]
}
现在已经有了5条数据.
现在我们来构建一个查询:
GET alice/user/_search
{
"query":{
"match": {
"name": "爱丽丝"
}
}
}
默认的话,es会查询出文档的所有字段,如果我们只想要部分的字段,就可以像下面所展示的demo进行查询:
GET alice/user/_search
{
"query":{
"match": {
"name": "爱丽丝"
}
},
"_source":["name","desc"]
}
如上例所示,在查询中,通过 _source 来控制仅返回 name 和 desc 属性。页面返回的查询结果如下:
3.4排序查询
我们说到排序,有人就会想到:正序或倒序。那么我们先来根据age字段倒序查询:
GET alice/user/_search
{
"query":{
"match": {
"name": "爱丽丝"
}
},
"sort": [
{
"age":
{
"order": "desc"
}
}
]
}
查询返回的结果如下:
同理,如果我们想要正序查询,只需要将desc换成了asc即可。
GET alice/user/_search
{
"query":{
"match": {
"name": "爱丽丝"
}
},
"sort": [
{
"age":
{
"order": "asc"
}
}
],
"from":0,
"size":1
}
查询结果如下:
注意:在排序的过程中,只能使用可排序的属性进行排序。那么可以排序的属性有哪些呢?
- 数字
- 日期
- ID
- 其他都不行!
3.5分页查询
学到这里,我们也可以看到,我们的查询条件越来越多,开始仅是简单查询,慢慢增加条件查询,增加排序,对返回结果进行限制。所以,我们可以说,对 于 elasticsearch 来说,所有的查询条件都是可插拔的。比如说,我们在查询中,仅对返回结果进行限制:
GET alice/user/_search
{
"query":
{"match_all": {}
},
"from":0, # 从第n条开始
"size":4 # 返回n条数据
}
分页查询类似于我们SQL中的 limit 语句。在 es 中我们想要实现这样的效果只需要用 from 指定 从第几条数据开始,size指定返回多少条数据即可。
3.6布尔查询
must (and)
我们上面已经讲过了通过构建查询的方法去做模糊查询,那我们如果想多条件查询
例如查询name为alice,并且age是25岁,那该如何查询呢?
我们通过在 bool属性内使用 must 来作为查询条件!看结果,是不是 有点像and的感觉,里面的条件需要都满足!
GET alice/user/_search
{
"query":{
"bool": {
"must":[
{
"match":{
"name":"爱丽丝"
}
},
{
"match":{
"age":25
}
}
]
}
}
}
查询结果如下
should (or)
那么我要查询name为爱丽丝或 age 为 25 的呢?
我们只需要将boolean属性内的must值换成should 即可,这就有点相当于 or 的感觉
GET alice/user/_search
{
"query":{
"bool": {
"should":[
{
"match":{
"name":"爱丽丝"
}
},
{
"match":{
"age":25
}
}
]
}
}
}
查询结果如下
must_not (not)
那现在我想要查询年龄不是 25 的 数据,只需要将boolean的属性值换成must_not即可
GET alice/user/_search
{
"query":{
"bool": {
"must_not":[
{
"match":{
"age":25
}
}
]
}
}
}
查询结果如下:
Filter
那如果查询 name 为爱丽丝,age 大于 24 的数据,需要使用到filter进行过滤。
GET alice/user/_search
{
"query":{
"bool":{
"must": [
{
"match": {
"name": "爱丽丝"
}
}
],
"filter": [
{
"range": {
"age": {
"gt": 24
}
}
}
]
}
}
}
查询结果如下,可以发现只有age为25 和 26的两条数据。
这里就用到了 filter 条件过滤查询,过滤条件的范围用 range 表示,其余操作如下 :
- gt 表示大于
- gte 表示大于等于
- lt 表示小于
- lte 表示小于等于
那现在要查询,例如 age 在24到26之间的数据该如何查询?
GET alice/user/_search
{
"query":{
"bool":{
"filter": [
{
"range": {
"age": {
"gte": 24,
"lte": 26
}
}
}
]
}
}
}
查询结果:
3.7短语检索
为了方便测试,我们再加入几条文档数据:
PUT /alice/user/6
{
"name":"大数据老K",
"age":25,
"desc":"技术成就自我!",
"tags":["男","学习","技术"]
}
PUT /alice/user/7
{
"name":"Python女侠",
"age":26,
"desc":"人生苦短,我用Python!",
"tags":["靓女","勤奋学习","善于交际"]
}
例如现在需要查询tags中包含“男”的数据
GET alice/user/_search
{
"query":{
"match":{
"tags":"男"
}
}
}
查询结果如下:
3.8匹配多个标签
既然按照标签检索,那么,能不能写多个标签呢?
GET alice/user/_search
{
"query":{
"match":{
"tags":"男 学习"
}
}
}
此时我们可以观察返回的结果,可以发现只要满足一个标签就能返回这个数据了
3.9精确查询
term查询是直接通过倒排索引指定的词条进程精确查找的!
关于分词:
- term ,不经过分词,直接查询精确的值
- match,会使用分词器解析!(先分析文档,然后再通过分析的文档进行查询!)
说到分词器解析,就不得不提到两种数据类型:text和keyword。下面我们就来做个测试:
// 创建一个索引,并指定类型
PUT testdb
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"desc":{
"type":"keyword"
}
}
}
}
// 插入数据
PUT testdb/_doc/1
{
"name":"爱丽丝学大数据name",
"desc":"爱丽丝学大数据desc"
}
PUT testdb/_doc/2
{
"name":"爱丽丝学大数据name2",
"desc":"爱丽丝学大数据desc2"
}
上述中testdb索引中,字段name在被查询时会被分析器进行分析后匹配查询。而属于keyword类型不会被分析器处理。
我们来验证一下:
GET _analyze
{
"analyzer": "keyword",
"text": "爱丽丝学大数据 name"
}
是不是没有被分析~就是简单的一个字符串啊。再测试一下:
GET _analyze
{
"analyzer": "standard",
"text": "爱丽丝学大数据 name"
}
查询结果:
然后我们可以得出结论:keyword 字段类型不会被分析器分析!
下面我们用前面添加的2条数据做测试:
先精准查询text类型的字段
GET testdb/_search // text 会被分析器分析 查询
{
"query": {
"term": {
"name": "爱"
}
}
}
查询结果,2条数据都能匹配到。
然后用standard类型做精准测试
GET testdb/_search // keyword 不会被分析所以直接查询
{
"query": {
"term": {
"desc": "爱丽丝学大数据desc"
}
}
}
查询结果,只有1条数据能匹配到。
3.10查找多个精确值精确
为了方便测试,我们再添加如下数据:
PUT testdb/_doc/3
{
"t1":"22",
"t2":"2021-03-01"
}
PUT testdb/_doc/4
{
"t1":"33",
"t2":"2021-03-01"
}
然后进行查询
GET testdb/_search
{
"query": {
"bool":{
"should": [
{
"term": {
"t1":"22"
}
},
{
"term": {
"t1":"33"
}
}
]
}
}
}
查询结果:
可以发现2条数据也都能查到,证明就算是term精确查询,也能够查询多个值。
当然,除了 bool 查询之外,下面这种方式也同样是可以的。
GET testdb/_doc/_search
{
"query":{
"terms":{
"t1":["22","33"]
}
}
}
下面要介绍的功能,就是经常被搜索引擎用到的“高亮显示”!
3.11高亮显示
我们可以通过highlight属性,来对我们查询的结果的指定字段做高亮显示!
GET alice/user/_search
{
"query":{
"match": {
"name": "爱丽丝"
}
},
"highlight":{
"fields": {
"name": {}
}
}
}
观察返回的结果,我们可以发现搜索相关的结果,被加上了高亮标签<em>
现在效果看到了,那我们有没有办法自定义样式呢?
答案当然是可以的,我们需要在pre_tags中定义标签的前缀,post_tags中定义后缀!
GET alice/user/_search
{
"query":{
"match": {
"name": "爱丽丝"
}
},
"highlight":{
"pre_tags": "<b class='key' style='color:red'>",
"post_tags": "</b>",
"fields": {
"name": {}
}
}
}
查询结果:
四、ES命令常用关键字【精】
URL【路径】
_search _analyze _update _cluster
Pretty q= ? * health
JSON【传参】
_source
Query match match_all match_phrase match_phrase_prefix muti_match
Term terms
Doc _mget docs
Bool must must_not should
Filter range
From size from/to/include_lower/include_upper
Sort order
Highlight fields pre_tags post_tags
Mappings properties type
Analyzer keyword standard
忘记自己建的索引名字:可以使用通配符*
GET Alic*/_search
通过url搜索
GET Ali*/_search?q=name:爱丽丝
关键字【总结】
_search
_analyze
Pretty
Q=
*
Query
Term
Terms
From
Size
Match
Match_all
Muti_match可以指定多个字段
Match_phrase短语匹配查询
Match_phrase_prefix
_source
Sort
Order
From 、to、include_lower、include_upper
Wildcard 允许使用通配符* 和 ?来进行查询
*代表0个或多个字符
?代表任意一个字符