整合Elasticsearch和商品的上架
- 一、整合ES
- ES常用概念
- 索引,类型,文档是什么?
- 倒排索引
- 相关度分数score的计算
- 安装ES和Kibana
- 快速安装
- ES
- kibana
- 初步检索_cat
- ES的增删改查
- 新增文档
- put新增
- post新增
- 区别
- 查询文档
- 乐观锁的使用
- 更新文档_update
- 删除文档或索引
- ES的批量操作_bulk
- 执行多条数据
- 样本测试数据
- ES进阶
- 两种查询方式
- QueryDSL
- 常用查询
- match全文检索
- query/bool/must复合查询
- aggs/agg1(聚合)
- Mapping字段映射
- 分词
- 自定义词库
- 安装Nginx
- 整合ElasticSearch
- 配置类
- 测试类
- 保存数据
- 简单的检索
- 复杂的查询
- 二、整合商品上架
- sku在es中存储
- 最终选用的数据模型
- 构造基本数据
- 库存量查询
- 商品上架
- 完整代码
- 详解
- 获得spu对应的sku集合
- 获得spu的基础属性实体集合
- 给SkuEsModel.Attrs对象赋值
- 查询是否有库存
- mall-search保存数据
- 将数据发送给ES保存
- DeBug调式
- 获得spu对应的sku集合
- 获得spu的基础属性实体集合
- 给SkuEsModel.Attrs对象赋值
一、整合ES
ES常用概念
索引,类型,文档是什么?
- 索引就像是Mysql中的库
- 类型就像是Mysql中的表
- 文档就像是数据
- 属性就是列名
- 所有的数据都是Json格式
倒排索引
简约理解版本2.0
正向索引
,数据库创建索引,增加搜索速度。
倒排索引
是根据关键字去找文档,然后记录一下出现的位置和次数。
根据关键字去找文档,然后记录一下出现的位置和次数
什么是倒排索引?
ElasticSearch中一个重要的概念 : 倒排索引(Inverted Index)也叫反向索引
,有反向索引必有正向索引。通俗地来讲,正向索引是通过key找value,反向索引则是通过value找key
。
首先弄懂几个概念,如果类比现代汉语词典的话,那么Term
就相当于词语
,Term Dictionary
相当于汉语词典本身
,Term Index
相当于词典的目录索引
,Posting List
相当于词语在字典的页数集合
:
- Term(单词):一段文本经过分析器分析以后就会输出一串单词,这一个一个的就叫做Term(直译为:单词)
- Term Dictionary(单词字典):顾名思义,它里面维护的是Term,可以理解为Term的集合
- Term Index(单词索引):为了更快的找到某个单词,我们为单词建立索引。B-Tree通过减少磁盘寻道次数来提高查询性能,
- Elasticsearch也是采用同样的思路,直接通过内存查找term,不读磁盘,但是如果term太多,term dictionary也会很大,放内存不现实,于是有了Term Index,就像字典里的索引页一样,A开头的有哪些term,分别在哪页,可以理解term index是一颗树:
- Posting List(倒排列表):倒排列表记录了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。(PS:实际的倒排列表中并不只是存了文档ID这么简单,还有一些其它的信息,比如:词频(Term出现的次数)、偏移量(offset)等,可以想象成是Python中的元组,或者Java中的对象)
相关度分数score的计算
ES工作原理还是很复杂的,现阶段我们学会使用即可,到后期准备面试了,在继续深入学习!
安装ES和Kibana
快速安装
ES
查看elasticsearch版本信息: http://192.168.1.8:9200
kibana
访问Kibana: http://192.168.1.8:5601/app/kibana
初步检索_cat
ES的增删改查
新增文档
put新增
新增文档也就是新增数据库中的表,保存的时候用唯一的标识指定
http://192.168.1.8:9200/customer/external/1
post新增
不带id
带上id,也是新增修改二合一
区别
PUT必须指定id;由于PUT需要指定id,我们一般用来做修改操作
POST新增。如果不指定id
,会自动生成id,指定
存在的id为更新
查询文档
GET /customer/external/1
乐观锁的使用
通过“if_seq_no=1&if_primary_term=1
”,当序列号匹配的时候,才进行修改,否则不修改。
开启事务锁后,两个事务并行修改数据的时候,只要有一个事务修改完数据,记录中记录的版本号就会加1,另一个事务修改的时候会带上版本号判断,如果版本号发生了变化了那就不会进行修改
更新文档_update
不同:带有update情况下
- POST操作会对比源文档数据,如果相同不会有什么操作,文档version不增加。(带乐观锁)
- PUT操作总会重新保存并增加version版本(不带)
删除文档或索引
注:elasticsearch并没有提供删除类型的操作,只提供了删除索引和文档的操作。
ES的批量操作_bulk
执行多条数据
这些数据之间是相互独立的,一条失败,不会影响其他数据
样本测试数据
https://gitee.com/xlh_blog/common_content/blob/master/es%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE.json#
ES进阶
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/docs-reindex.html
官方文档如下:
如下示例看着文档就可以写出来。
两种查询方式
ES支持两种基本方式检索;
- 通过REST request uri 发送搜索参数 (uri +检索参数);
- 通过REST request body 来发送它们(uri+请求体);
测试如下:
QueryDSL
QueryDSL就是我们第二种查询方式中请求体的内容
它的格式如下:
常用查询
match全文检索
match_phrase短语匹配
规则如下:
query/bool/must复合查询
能够看到相关度越高,得分也越高。
query/filter【结果过滤】
- must 贡献得分
- should 贡献得分
- must_not 不贡献得分
- filter 不贡献得分
匹配的越多,得分越高!只有must和should影响相关性得分
query/term
aggs/agg1(聚合)
nested对象聚合
Mapping字段映射
映射定义文档如何被存储和检索的
Mapping映射是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和索引的。比如:使用mapping来定义:
当然对于已经存在的字段进行映射的时候,我们不能进行更新。更新必须创建新的索引才行
更新索引
对于已经存在的字段映射,我们不能更新。更新必须创建新的索引,进行数据迁移。
数据迁移
分词
ES中的分词就是能接收一个字符流,将之分割为独立的token(词元,通常是独立的单词),然后输入tokens流
简单来说,ES解析你的输入,是一个一个字的去读取
如下:
使用分词器
关于分词器: https://www.elastic.co/guide/en/elasticsearch/reference/7.6/analysis.html
对于中文,我们需要安装额外的分词器
下载地址:
https://github.com/medcl/elasticsearch-analysis-ik/releases
将上面下载的插件,放到es的plugs文件夹下
重启ES,测试分词器是否安装成功
如果可以解析中文成功,证明安装成功
调整虚拟机内存为3G
自定义词库
上面ES虽然能识别中文了,但是也是一个一个字识别的,我们想让它按我们的规矩来的话,我们需要自定义一下
默认es是不支持一些新的词语,它的词库里组合是很不规律的
那么我们可以自定义扩展一下这个词库!怎么扩展呢?
安装Nginx
将分词内容放到nginx服务器当中
修改ESIKAnalyzer.cfg.xml
修改完成后,需要重启es容器,否则修改不生效。
当然后面有新词的话,那就直接在es指定目录下继续添加新词即可
整合ElasticSearch
我们先来一个简单的写写试试
配置类
固定写法,其中builder = RestClient.builder(new HttpHost(“192.168.1.8”, 9200, “http”));如果有ES集群的话可以用来指定多个ES主机地址
测试类
保存数据
测试的时候要在启动类上排除数据库
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
简单的检索
复杂的查询
执行结果如下:
和我们Kibana显示的结果也对应上了
二、整合商品上架
需求:
- 在后台选择上架的商品才能在网站展示
- 上架的商品也可以被ES检索
sku在es中存储
商品上架在es中应该是存储spu
- 检索的时候输入名字,这样就是需要用sku的title去全文检索
- 检索使用商品规格,规格是spu的公共属性,每个spu是一样的
方案1:
缺点:如果每个sku都存储规格参数(如尺寸),会有冗余存储,因为每个spu对应的sku的规格参数都一样
方案2:
先找到4000个符合要求的spu,再根据4000个spu查询对应的属性,封装了4000个id,long 8B*4000=32000B=32KB
1K个人检索,就是32MB
结论:如果将规格参数单独建立索引,会出现检索时出现大量数据传输的问题,会引起网络网络
所以我们选方案一,用空间换时间
最终选用的数据模型
{ “type”: “keyword” }, 保持数据精度问题,可以检索,但不分词
“analyzer”: “ik_smart” 中文分词器
“index”: false, 不可被检索,不生成index
“doc_values”: false 默认为true,不可被聚合,es就不会维护一些聚合的信息
这个数据模型要先在es中建立
构造基本数据
商品上架需要在es中保存spu信息并更新spu状态信息,所以我们就建立专门的vo来接收
SkuEsModel
写在common模块
库存量查询
上架的话需要确定库存,所以调用ware微服务来检测是否有库存
impl
实现也比较好理解,就是先用自定义的mapper查有没有库存
有的话,给库存赋值,并收集成集合
自定义mapper
这里的库存并不是简单查一下库存表,所以不能用mp自带的api去查询,需要自定义一个简单的sql
就是用库存减去锁定的库存即可得出!
商品上架
完整代码
controller
impl
详解
获得spu对应的sku集合
其中skuInfoService.getSkusBySpuId
中getSkusBySpuId
就是写一个简单的query因为是单表查询
所以应该这样写
获得spu的基础属性实体集合
给SkuEsModel.Attrs对象赋值
找出所有属性的id集合
在上面的结果中进行筛选,得到可搜索ID
其中attrService.selectSearchAttrs实现如下:
这里的baseMapper.selectSearchAttrIds
,baseMapper是MP提供的类,里面很多常见的CRUD,如果要扩展的话,可以直接写扩展的方法名,让写对应的Mapper就行,很方便,安装插件就可以一键生成!
筛选每个属性,下面sql的意思就是普通的查询加上可搜索id的条件
给SkuEsModel.Attrs对象赋值
查询是否有库存
这里属于跨微服务的调用,所以这里使用了openfeign去远程调用ware微服务去查询是否有库存
ware微服务
里的查询库存
方法为:
实现如下:
通过skuid来查询该sku的库存数
getSkuStock属于自定义的查询接口,内容如下:
那么接下来product微服务
就可以使用该方法进行查询了,内容如下:
对所有sku进行封装
就是对要保存的sku封装成功ES的VO模型进行接收
mall-search保存数据
controller
impl
将数据发送给ES保存
- FeignService
- 通过FeignService远程调用
DeBug调式
商品上架用到了三个微服务,分别是product、ware、search
那我们分别debug启动它们,然后在这些微服务中使用的方法中打上断点,查看调用流程
获得spu对应的sku集合
获得spu的基础属性实体集合
基础属性如下:
给SkuEsModel.Attrs对象赋值
测试