拉呱,无论是当作全文检索工具,还是仅仅当作NOSQL,Elasticsearch的性能,牛的没法说!!!奈何和它相见恨晚
一. 使用场景
全文检索-像淘宝京东类似的网上商城,当我们在在搜索框搜索某个商品名称时,网络没有问题的话,获取响应的速度,几乎和我们键盘起落的速度是一致的,这足以ES的魅力,亿万级别的搜索,秒秒钟完事
一个正规的项目运行过程中,日志的产出是源源不断的,把日志的文本信息导入到ES,使用它的聚合功能轻松获取我们关系的内容,结合Kibana做图表分析,可视化日志记录,动态分析
二. 基本概念
- 2.1 什么是是全文检索
全文检索就是计算机程序通过扫描文章中的每一个词,对必要的词建立一个索引(上篇博客我们使用ik分词器,帮助es分词),记录下这个词在文章中出现的位置和次数,这样当用户去查询的时候,他可以根据索引极快的查找出相应的内容
稍微解释下, 全文检索可以片面的理解成是从敲代码的人的角度上说的,假如你是用户,你会关心什么全文检索? 用户关系的是搜索结果!那好,对我们敲代码的来说,就是1.拿到用户搜索框输入的关键字送进ES (数据在存进es的时候,es对他们进行了索引)2.ES的程序会对关键字进行分词,拿着这些碎片去ES中的索引库里面的type中的field中匹配比对,那,那么多field,它和谁比对呢? 和标记上text属性的字段比对)
另外要了解的内容1.全文检索只处理文本不处理语义,2.全文检索忽略英文的大小写 3.结果可能不止一个,并且有相关得分
- 2.2 与关系型数据库名称上的对比
mysql | Elasticsearch |
---|---|
Database 数据库 | indices 索引 |
Table 数据表 | type 类型 |
Row 行 | document 文档 |
Columns 列 | Field 字段 |
其实大多数人都是先接触的关系型数据库,我也是,可能一开始感觉着好别扭,但是再回头来看,好像他的名字比传统的关系型数据库名什么行啊列啊,更合理一些
- 2.3 分片(shard)和副本(replica)
通过图片可以看到: 数据分成三部分,分别存放在三个分片里面,而且每个分片都有自己的副本,这样就算是一台es,它同样是分布式
二. 怎么玩?
这部分纯属扯皮了,不说语法,说一下我对它的感觉,首先呢, 为什么学它? 图方便快速呗,大部分情况下,是需要使用它的全文检索功能,但是总得有个下手点吧,不用说一开始都是环境配置一顿整,访问个9200看到版本号,也算是开个头了,然后呢? 先不用想他怎么检索,怎么花里胡哨的检索,我们得知道自己想检索什么!先把数据给它,思路就来了,先去搞数据,下一步自然就是创建新的索引(数据库),循环把我们的数据送进es的索引里面.到这里,也算是完成一半的任务了,下面就是使用人家提供好的api去索引库,检索就好了. 先有个大概的思路.想干什么,怎么干,往下学
三. 原生语法
- elasticsearch采用的REST风格的api,也就是说,其实他就是一次http的请求
下面会有一些关键字 也就是json的 key部分,对我们来说,一般可以见名知意
3.1.1 创建索引库
- 请求方式: PUT
- 请求路径: /索引库名
- 请求参数: json
1 | PUT /xiaowu1 |
成功的相应:
1 | { |
响应:相关的源信息
1 | { |
3.1.3 删除索引库
- 请求方式: DELETE
- 格式: DELETE /索引库名
3.2.1 配置映射
在玩搜索之前我们要思考如何定义文档的,如文档包括哪些字段,这些字段是否保存,是否索引,是否分词等等, 实际上就像是在描述我们数据库中的一张表,有哪些列,列是什么类型的,是主键不?自增长不?
以后看到”mappings”:{} 就像查看关系型数据库中对整张表的定义一样
- 请求方式: PUT
- 格式: PUT /索引库名/_mapping/类型名
1 | PUT /xiaowu/_mapping/goods |
1 .字符串类型有两种
- text : 可检索
- keyword : 不可检索
2 .数值类型
- 基本数值类型: long ,float,byte,double…
- 浮点数高精度类型: scaled_float
- 它需要我们指定一个精度因子,比如10,100,es把真实值乘以这个精度因子存储,取出后还原,(商品价格)
- 日期类型:
- Date
- 存储对象
1 | {girl:{name:"张三",age:23} |
而是会把它处理成 girl.name和girl.age
5 .store
- 表示,是否将数据额外存储,意思是,如果不存储,用户就搜索不出来
- 在solr中如果为store设为false,那么用户就搜索不出来这个字段
- 但是elasticsearch中,并不用store的值控制是否可以被搜索出来,即便他是false,用户仍然可以搜索出结果,原因是elasticsearch的底层,在创建文档时,会将文档的原始数据备份保存到一个叫 _source的属性中,而我们可以通过过滤_source选择那些需要显示,那些不需要显示,这样如我们把store的值设置为ture它就会多储存一份,得不偿失(store相当于作废了)
3.2.2 向已经存在的索引库中添加数据(不一定要指定ID)
- 请求方式: POST
- 格式: POST /索引库名/类型名{}
1 | POST /changwu/item/ |
响应:
1 | { |
id,是自动生成的当前数据的标识,我们还可以指定
1 | POST /changwu/item/2 |
3.2.3 基本查询
- 请求方式: GET
1 | GET /索引库名/_search |
- query是Elasticsearch内置的一个对象,里面有不同的查询属性
使用,SpringDataElasticsearch玩复杂查询的时候,免不了会BuildQueryXXX
- 查询类型:
- match_all 查询所有
- match
- term
- range
- 查询条件根据类型的不同而不同,(就像关系型数据库中表的字段不同…)
3.2.4 匹配查询 match
or 关系
match类型查询,会把查询条件进行分词,然后再查询,词条之间是or关系,按照相关性得分排序
1 | GET /goods/_search |
and关系
很多情况下我们希望更精确的查找,于是我们使用 and关系
1 | GET /xiaowu/_search |
这样他在分词的时候,米–手机同时都匹配上才会显示出结果!
假设有这样一种情况,用户给定的条件分词后,有五个词,但是其中的四个是在描述他想要搜索的内容,如果使用or,毫无疑问,一大堆杂七杂八的东西被查询出来,如果使用and,es很可能把那个目标文档排除,那该怎么办呢?看下面!
- match支持使用minimum_should_match 最小匹配参数,通常设置为一个百分数
1 | GET /xiaowu/_search |
意思是,用户输入的词条满足75%的匹配程,我就认为是匹配上了, —用户输入的检索条件,被分解为三个词,3*0.75=2.25 也就是说,这三个词,至少有两个是匹配上的,es就认为匹配成功
3.2.5多字段查询 muti_match
- muti_match和match一样,但是不同的是它可以同时在多个字段中检索
1 | GET /xiaowu/_search |
他的fields接受一个字段数组
3.2.6 词条查询(term)
- 和前面的查询条件不同的是term,它被用作精确查询,比如数字,时间,布尔,和字段属性为keyword类型的关键字
1 | GET /xiaowu/_search |
3.2.7 多词条精确匹配
- 和term一样的精确匹配,但是不同的是它支持同时使用多个词条进行精确匹配,如果只要索引库中的文档包含指定值中的任意一个,都算作满足条件
3.3.1 结果过滤
默认情况下,elasicsearch在搜索的结果在,会把文档保存在_source里面的所有字段都返回,如果我们想获取里面的部分结果,添加_soure过滤
1 | GET /xiaowu/_search |
- 另外,_source里面还有两个属性
- “includes”:[想显示的字段]
- “excludes”:[不想显示的字段]
解读查询结果:
1 | { |
- 在所有的index里面查询指定的字段
1 | GET _search |
3.4.1 智能判断
- 新增属性的时候,可以添加新的字段
1 | POST /xiaowu/goods/ |
查询结果:
1 | { |
查看映射
1 | { |
这样看,其实我们在上面配置的映射关系就没有必要了,Elasticsearch会智能化的推断出字段的type,比如true它推断为boolean, 但是有问题的是title 它推断为 text ,title.keyword是 keyword类型
3.5.1 修改数据(指明ID)
把上面的POST转变成PUT就是修改
- id对应的文档存在则修改
- id对应的文档不存在则新增
3.6.1 删除数据
语法: DELETE /索引库名/类型名/id
3.7.1 高级玩法-布尔组合
看得懂,会使用Elasticsearch的高级玩法很重要,这关系着,能不能理解如何使用它的原生api进行高级查询
- 布尔组合,结合了其他的查询,实现了这样的功能, – 我的搜索结果中,一定含有谁(must),一定不含有谁(must_not),可能含有谁(should)
1 | GET /xiaowu/_search |
3.7.2 高级玩法–范围查询range
- range实现的是查询出满足某个区间的结果
1 | GET /xiaowu/_search |
关键字 | 含义 |
---|---|
gte | 大于等于 |
lte | 小于等于 |
gt | 大于 |
lt | 小于 |
3.7.3 模糊查询—fuzzy
模糊查询很人性化,它允许用户在编辑的条件有拼写错误,但是出现偏差的编辑差距不得超过2
1 | GET /xiaowu/_search |
- 这样查询是可以查询出apple的
- “fuzziness”: 限定编辑距离
3.7.4 过滤—filter
- 1.有条件查询进行过滤
查询就会影响文档的相关性得分,假如我们只是想在现有的查询基础上,根据需求过滤下一些结果,却不想影响文档的相关性得分的话filter就派上用场了
1 | GET /changwu/_search |
filter中还可以在bool组合过滤
- 2.无条件查询进行过滤
如果一次查询只有过滤,没有查询条件,不希望进行评分,我们可以使用constant_score
1 | GET /changwu/_search |
3.7.5 — 排序 sort
排序sort和query是有先后顺序的,先query,再排序
- 单字段排序
1 | GET /changwu/_search |
- 多字段排序
看上面的sort后面的条件,是个数组,因此我们可以写条件,按多个字段进行排序
聚合:aggregations
- 基本概念,两个新概念,在Elasticsearch中的聚合分为两个部分,聚合为 桶bucket和度量
- 桶就像mysql中的分组查询,比如说学号相同,姓名相同的肯定是同一个人,我们就把它当成一组,在这里就是一个桶
- 度量–以每个桶为基础,做运算
当然Elasticsearah里面分桶的方式很多
- Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组,这个范围要手动告诉他,0-10 10-15 15-30等
- Histogram Aggregation:根据数值阶梯(柱状图)分组,与日期类似, 告诉他一个段就行了,她会自动的分组
- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
常用的度量方法:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同时返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前几
- Value Count Aggregation:求总数
es中进行过滤,排序,聚合的字段,不能被分词!!!!*
1 | GET /cars/_search |
一般都是先聚为桶,然后在桶的基础上进行度量
四.SpringDataElsticsearch
这是原来画的图,JEST直接放弃了,让我们自己拼接json串,简直只有难受
终于到编码阶段,这部分相比前面的原生api看起来就好受多了,Spring一整合,啥东西都简单的只剩下两件事,1,写个配置文件,2.用它的方法,玩他的注解–, 当然我现在回顾学习的整个过程,最重要的是还是那句话,不要忘记了自己的需求,不然学着学着容易迷失,不知道自己想干啥,对于Elasticsearch吧先觉的它啥都能干,又觉得它啥也干不了,这是个很尴尬的事情! 那,我用它做全文检索,我就得去搞明白 知道下面那几件事
- 怎么搭建起开发环境,使我的java代码和ES交互
- spring整合它嘛,提供了哪些注解,表示我的javaBean是个document对象
- 如何建立索引库
- 基本的CRUD
- 花里胡哨的查询方法
下面挨个做这几件事!
4.1 搭建开发环境
坐标
1 | <dependency> |
配置文件(java和它的交互走的是tcp协议)
1 | spring: |
启动类
4.1 实体类及注解
实体类在java中就像是接盘侠,啥样的东东,它都有能给接下来,看完了下面的注解,就知道了如何把数据存进es认识的实体类,准备把他们存在索引库
1 | import org.springframework.data.annotation.Id; |
- @Document
- 作用在类,标记实体类为文档对象
- indexName:对应索引库名称—- 数据库名
- type:对应在索引库中的类型—- 表名
- shards:分片数量,默认5
-replicas:副本数量,默认1
- @Id
- 作用在成员变量id上
- 标记一个字段作为id主键(这个id别写错了,不然程序都启动不起来)
- @Field
- 作用在成员变量,标记为文档的字段,并指定字段映射属性:
- type:字段类型,取值是枚举:FieldType
- index:是否索引,布尔类型,默认是true
- store:是否存储,布尔类型,默认是false
- analyzer:分词器名称
另外不要忘记了它的智能推断,如果我们不在字段上添加@Field,他根据值的类型进行推断
比如: 下面三个都会被推断为long类型,
1 | private Long cid3;// |
4.2 创建索引库
索引库的创建放到text里面就ok了
- 使用的是ElasticsearchTemplate,Spring一直都是这样,整合完了,给你个模板
创建索引,添加映射,删除索引
1 | template.createIndex(Goods.class); |
4.3 基本的CRUD
SpringData的强大之后就是,我们不再去写dao层了,她会通过反射给我们写好,有点Mybatis里面的通用mapper的意思,但是它更强大,—你给方法名它自动的生成方法
1 | public interface GoodsRepository extends ElasticsearchRepository<Goods,Long> {} |
使用 repository点一下,findXX saveXXX, deleteXXX全出来了,不再细说
如果可以看懂原生的语法,那么对他的使用就不多说了…就是无脑使用
到了这,就知道了如何把通过java代码,对索引库里面的数据进行简单的增删改查
4.4 着重看一下如何进行繁杂查询
真正使用es的时候,Repository里面的方法可以满足大部分的功能,但是聚合,过滤的话,只能使用原生的API
假设我们有下面的需求: 前端把用户需要搜索的信息收集起来了–全文检索
- 全文检索
1 | /** |
- 聚合查询
1 | /** |