Elasticsearch
基于java编写的Lucence的一层封装
基本概念
Elasticsearch是面向文档的,关系行数据库
- 索引(indices):对应mysql中的数据库
- types: 对应表
- documents: 对应mysql中行
- fields: 对应mysql中的字段
物理设计:
elasticsearch 在后台把每个索引划分成多个分片,每份分片可以在集群中的不同服务器间迁移
逻辑设计:
一个索引类型中,包含多个文档,比如说文档1,文档2。 当我们索引一篇文档时,可以通过这样的一个顺序找到 它: 索引 ▷ 类型 ▷ 文档ID ,通过这个组合我们就能索引到某个具体的文档。 注意 : ID不必是整数,实际上它是个字符串。
文档
文档就是一条一条的数据
elasticsearch 是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,elasticsearch 中,文档有几个 重要属性 :
自我包含,一篇文档同时包含字段和对应的值,也就是同时包含 key:value!
可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的!其实就是个JSON对象
灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在 elasticsearch 中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段。
尽管我们可以随意的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整形。因为 elasticsearch 会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在 elasticsearch 中,类型有时候也称为映射类型。
类型
类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。 类型中对于字段的定义称为映射,比如 name 映 射为字符串类型。 我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么 elasticsearch 是怎么做的呢?elasticsearch会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,elasticsearch就开始猜,如果这个值是18,那么elasticsearch会认为它是整形。 但是elasticsearch也可能猜不对, 所以最安全的方式就是提前定义好所需要的映射,这点跟关系型数据库殊途同归了,先定义好字段,然后再使用。
总之类型就是数据字段对应的数据类型的映射
索引
索引是映射类型的容器,elasticsearch 中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。 然后它们被存储到了各个分片上了。 我们来研究下分片是如何工作的。
物理设计 :节点和分片如何工作
一个集群至少有一个节点,而一个节点就是一个 elasricsearch 进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有个5个分片 ( primary shard ,又称主分片 ) 构成的,每一个主分片会有一个副本 ( replica shard ,又称复制分片 )
下图是一个有3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉 了,数据也不至于丢失。
实际上,一个分片是一个 Lucene 索引,一个包含倒排索引的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字
倒排索引
elasticsearch 使用的是一种称为倒排索引的结构,采用Lucene倒排索引作为底层。这种结构适用于快速的全文搜索, 一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。 例如,现在有两个文档, 每个文档包含如下内容:
Study every day, good good up to forever # 文档1包含的内容 |
为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档 :
现在,我们试图搜索 to forever,只需要查看包含每个词条的文档
两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回。
再来看一个示例,比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构 :
如果要搜索含有 python 标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要 查看标签这一栏,然后获取相关的文章ID即可。完全过滤掉无关的所有数据,提高效率!
在 elasticsearch 中, 索引这个词被频繁使用,这就是术语的使用。在 elasticsearch 中,索引被分为多个分片,每份分片是一个 Lucene 的索引。所以 一个 elasticsearch 索引是由多个Lucene索引组成的。
基本操作
IK分词器
分词:即把一段中文或者别的内容划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,是因为数据库中或者索引库中的数据也会进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如 “我爱大数据” 会被分为”我”,”爱”,”大”, “数”,”据”,这显然是不符合要求的,所以我们需要安装中文分词器 ik 来解决这个问题。
IK提供了两个分词算法:ik_smart 和 ik_max_word ,其中 ik_smart 为最少切分,ik_max_word 为 最细粒度划分
安装:https://github.com/medcl/elasticsearch-analysis-ik/
安装对应版本并放入plugin目录下
使用:
ik_smart模式:
GET _analyze
{
"analyzer":"ik_smart",
"text":"中国共产党"
}效果:
ik_max_word模式
GET _analyze
{
"analyzer":"ik_max_word",
"text":"中国共产党"
}效果:
Rest风格
什么是 Rest 风格呢?
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
其中 基本 的 Rest 命令说明:
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档 id ) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机文档 id ) |
POST | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称/文档id | 通过文档id查询文档 |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有数据 |
关于索引的基本操作
基础测试
- 创建一个索引
PUT /索引名/类型名/文档id{请求id}
// 命令解释 |
如下图所示:返回结果 (是以REST ful 风格返回的 ):
那么 name 这个字段用不用指定类型呢。毕竟我们关系型数据库 是需要指定类型的啊
字段类型
elasticsearch 常见的字段类型如下:
- 字符串类型
- 数值类型
long, integer, short, byte, double, float, half_float, scaled_float
- 日期类型
- 布尔值类型
- 二进制类型
- 等等
PUT新增设置索引规则(设置字段与数据类型)
PUT /test2 |
输出如下,说明创建成功了
{ |
GET 查看规则信息
现在我们来尝试使用一下 GET 命令,请求具体的信息!
可以发现通过GET请求,我们能够详细获取到该索引下具体的信息,其中包含字段类型。那上面示例中字段类型是我自己定义的,那么我们不定义类型会是什么情况呢?
我们首先发起一个PUT请求,创建一个新的索引 test3,并添加一条数据
PUT /test3/_doc/1 |
然后通过GET请求,可以发现非常的智能。但是如果我们的文档字段类型没有指定,那么es就会给我们默认配置的字段类型!
UPDATE修改
那如果我们想要修改文档里的字段信息呢?我们可以选择 UPDATE 也可以 选择 PUT进行覆盖
例如我可以像下图中的例子,将之前test3索引中的1号文档中的 name 字段修改后,重复提交,发现更新成功,但是注意 version 版本号已经变成了2但是注意这种方法有弊端,如果我们在PUT的过程中,遗漏了字段,那么数据就会被新数据覆盖!所以,修改数据不建议使用PUT覆盖的方式!
我们使用 POST 命令,在 id 后面跟 _update ,要修改的内容放到 doc 文档(属性)中即可。可以发现此时更新之后的version变成了3。所以,一旦索引被创建了之后,所有的修改都可以通过版本号看到变化。
DELETE删除
结论: 通过 DELETE 命令实现删除,根据请求判断是删除索引还是删除文档记录!因此,使用 RESTFUL 风格是我们学习ES值得推荐使用的
其他命令
我们可以通过GET _cat/health来获取集群的一个健康状态除了看集群的健康信息,
通过命令GET _cat/indices?v,我们可以获取到当前索引的很多信息,返回值包括所有索引的状态健康情况,分片,数据储存大小等等
关于文档的基本操作
接下来我们学习关于文档的基本操作,首先先重新创建一个新的索引,并添加一些数据
PUT /alice/user/1 |
接下来就可以进行文档的基本操作了!
简单查询
通过 GET 命令,我们可以搜索到指定 id 的文档信息
GET alice/user/1
当然这是简单的搜索,下面我们来看一下 es 如何做条件查询
条件查询_search?q=
我们可以通过如下命令,来进行条件查询
GET alice/user/_search?q=name:张三
我们看一下结果 返回并不是 数据本身,是给我们了一个 hits ,还有 _score得分,就是根据算法算出和查询条件匹配度高的分就越高。
我们在以某度为例的搜索引擎上进行搜索也是一样的道理,权重越高网站的位置就越靠前!
但我们一般使用不会直接加条件去查询,更多的会用到下面要介绍到的复杂操作搜索。
复杂操作搜索 select( 排序,分页,高亮,模糊查询,精准查询!)
为了方便测试,我又执行下面的命令,往Alice索引下添加了2个文档
PUT /alice/user/4 |
现在我们来构建一个查询:
GET alice/user/_search |
默认的话,es会查询出文档的所有字段,如果我们只想要部分的字段,就可以像下面所展示的demo进行查询:
GET alice/user/_search |
如上例所示,在查询中,通过 _source 来控制仅返回 name 和 desc 属性。页面返回的查询结果如下:一般的,我们推荐使用构建查询,以后在与程序交互时的查询等也是使用构建查询方式处理查询条件,因为该方式可以构建更加复杂的查询条件,也更加一目了然。
排序查询
我们说到排序,有人就会想到:正序或倒序。那么我们先来根据age字段倒序查询:
GET alice/user/_search |
查询返回的结果如下:同理,如果我们想要正序查询,只需要将desc换成了asc即可。
GET alice/user/_search |
查询结果如下:
注意:在排序的过程中,只能使用可排序的属性进行排序。那么可以排序的属性有哪些呢?
- 数字
- 日期
- ID
其他都不行!
分页查询
学到这里,我们也可以看到,我们的查询条件越来越多,开始仅是简单查询,慢慢增加条件查询,增加排序,对返回结果进行限制。所以,我们可以说,对 于 elasticsearch 来说,所有的查询条件都是可插拔的。比如说,我们在查询中,仅对返回结果进行限制:
GET alice/user/_search |
分页查询类似于我们SQL中的 limit 语句。在 es 中我们想要实现这样的效果只需要用 from 指定 从第几条数据开始,size指定返回多少条数据即可。
布尔查询
must (and)
我们上面已经讲过了通过构建查询的方法去做模糊查询,那我们如果想多条件查询,例如查询name为alice,并且age是25岁,那该如何查询呢?
我们通过在 bool属性内使用 must 来作为查询条件!看结果,是不是 有点像and的感觉,里面的条件需要都满足!
GET alice/user/_search |
查询结果如下should (or)
那么我要查询name为爱丽丝或 age 为 25 的呢?
我们只需要将boolean属性内的must值换成should 即可,这就有点相当于 or 的感觉
GET alice/user/_search |
查询结果如下
must_not (not)
那现在我想要查询年龄不是 25 的 数据,只需要将boolean的属性值换成must_not即可
GET alice/user/_search |
查询结果如下:Fitter
那如果查询 name 为爱丽丝,age 大于 24 的数据,需要使用到filter进行过滤。
GET alice/user/_search |
查询结果如下,可以发现只有age为25 和 26的两条数据这里就用到了 filter 条件过滤查询,过滤条件的范围用 range 表示,其余操作如下 :
- gt 表示大于
- gte 表示大于等于
- lt 表示小于
- lte 表示小于等于
那现在要查询,例如 age 在24到26之间的数据该如何查询?
GET alice/user/_search |
查询结果:
短语检索
为了方便测试,我们再加入几条文档数据:
PUT /alice/user/6 |
例如现在需要查询tags中包含“男”的数据
GET alice/user/_search |
查询结果如下:
匹配多个标签
既然按照标签检索,那么,能不能写多个标签呢?
GET alice/user/_search |
此时我们可以观察返回的结果,可以发现只要满足一个标签就能返回这个数据了
精确查询
term查询是直接通过倒排索引指定的词条进程精确查找的!
关于分词:
- term ,不经过分词,直接查询精确的值
- match,会使用分词器解析!(先分析文档,然后再通过分析的文档进行查询!)
说到分词器解析,就不得不提到两种数据类型:text和keyword。下面我们就来做个测试:
// 创建一个索引,并指定类型 |
上述中testdb索引中,字段name在被查询时会被分析器进行分析后匹配查询。而属于keyword类型不会被分析器处理。
我们来验证一下:
GET _analyze |
查询结果:是不是没有被分析~就是简单的一个字符串啊。再测试一下:
GET _analyze |
查询结果:然后我们可以得出结论:keyword 字段类型不会被分析器分析!
下面我们用前面添加的2条数据做过测试:
先精准查询text类型的字段
GET testdb/_search // text 会被分析器分析 查询 |
查询结果,2条数据都能匹配到然后用standard类型做精准测试
GET testdb/_search // keyword 不会被分析所以直接查询 |
查询结果,只有1条数据能匹配到
查找多个精确值
为了方便测试,我们再添加如下数据:
PUT testdb/_doc/3 |
然后进行查询
GET testdb/_search |
查询结果:可以发现2条数据也都能查到,证明就算是term精确查询,也能够查询多个值。
当然,除了 bool 查询之外,下面这种方式也同样是可以的。
GET testdb/_doc/_search |
下面要介绍的功能,就是经常被搜索引擎用到的“高亮显示”!
高亮显示
我们可以通过highlight属性,来对我们查询的结果的指定字段做高亮显示!
GET alice/user/_search |
观察返回的结果,我们可以发现搜索相关的结果,被加上了高亮标签现在效果看到了,那我们有没有办法自定义样式呢?
答案当然是可以的,我们需要在pre_tags中定义标签的前缀,post_tags中定义后缀!
GET alice/user/_search |
查询结果:
SpringBoot集成ES
依赖
<dependency> |
使用springboot
\<dependency> |
注意版本要对应
这里我使用最新的用法
配置
package com.yyjccc.eslearn.config; |
索引
创建索引
|
判断索引是否存在
void hasIndex() throws IOException { |
删除索引
void DeleteIndex() throws IOException { |
文档
实体类
package com.yyjccc.eslearn.entity; |
创建文档数据
void addDoc() throws IOException { |
判断是否存在文档
void hasDoc() throws IOException { |
获取文档内容
void getDoc() throws IOException { |
注意实体类User必须是标准的JavaBean
跟新文档数据
void updateDoc() throws IOException { |
删除文档
void deleteDoc() throws IOException { |
批量操作
void bulkDoc() throws IOException { |
查询
void searchDoc() throws IOException { |