谷粒商城--整合Elasticsearch和商品的上架

Posted 吃个小菜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了谷粒商城--整合Elasticsearch和商品的上架相关的知识,希望对你有一定的参考价值。

整合Elasticsearch和商品的上架

一、整合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

快速安装

(1)下载ealastic search(存储和检索)和kibana(可视化检索)
docker pull elasticsearch:7.4.2
docker pull kibana:7.4.2

(2)配置
# 将docker里的目录挂载到linux的/mydata目录中
# 修改/mydata就可以改掉docker里的
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data

# es可以被远程任何机器访问
echo "http.host: 0.0.0.0" >/mydata/elasticsearch/config/elasticsearch.yml

# 递归更改权限,es需要访问
chmod -R 777 /mydata/elasticsearch/

(3)启动Elastic search
# 9200是用户交互端口 9300是集群心跳端口
# -e指定是单阶段运行
# -e指定占用的内存大小,生产时可以设置32G
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \\
-e  "discovery.type=single-node" \\
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \\
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \\
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \\
-v  /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \\
-d elasticsearch:7.4.2 


# 设置开机启动elasticsearch
docker update elasticsearch --restart=always

(4)启动kibana:
# kibana指定了了ES交互端口9200  # 5600位kibana主页端口
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.1.8:9200 -p 5601:5601 -d kibana:7.4.2


# 设置开机启动kibana
docker update kibana  --restart=always

ES

查看elasticsearch版本信息: http://192.168.1.8:9200

kibana

访问Kibana: http://192.168.1.8:5601/app/kibana

初步检索_cat

GET /_cat/nodes	
查看所有节点。集群中会用到

GET /_cat/health
查看es健康状况

GET /_cat/master
查看主节点

GET /_cat/indices
查看所有索引 ,等价于mysql数据库的show databases;

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

POST customer/externel/1/_update

    "doc":
        "name":"111"
    

或者
POST customer/externel/1

    "doc":
        "name":"222"
    

不同:带有update情况下

  • POST操作会对比源文档数据,如果相同不会有什么操作,文档version不增加。(带乐观锁)
  • PUT操作总会重新保存并增加version版本(不带)

删除文档或索引

DELETE customer/external/1
DELETE customer

注:elasticsearch并没有提供删除类型的操作,只提供了删除索引和文档的操作。

ES的批量操作_bulk

执行多条数据

这些数据之间是相互独立的,一条失败,不会影响其他数据

kibana中执行
POST /customer/external/_bulk
"index":"_id":"1"
"name":"John Doe"
"index":"_id":"2"
"name":"John Doe"

#! Deprecation: [types removal] Specifying types in bulk requests is deprecated.

  "took" : 20,
  "errors" : false,
  "items" : [
    
      "index" : 
        "_index" : "customer",
        "_type" : "external",
        "_id" : "1",
        "_version" : 5,
        "result" : "updated",
        "_shards" : 
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        ,
        "_seq_no" : 10,
        "_primary_term" : 1,
        "status" : 200
      
    ,
    
      "index" : 
        "_index" : "customer",
        "_type" : "external",
        "_id" : "2",
        "_version" : 1,
        "result" : "created",
        "_shards" : 
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        ,
        "_seq_no" : 11,
        "_primary_term" : 1,
        "status" : 201
      
    
  ]


样本测试数据

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+请求体);

测试如下:

GET bank/_search/?q=*&sort=account_number:asc

说明: q=* # 查询所有 sort # 排序字段 asc #升序

GET bank/_search

  "query": 
    "match_all": 
  ,
  "sort": [
    
      "account_number": "asc"
    
  ]



添加新的查询条件
GET bank/_search

  "query": 
    "match_all": 
  ,
  "sort": [
    
      "account_number": "asc"
    ,
    
      "balance": "desc"
    
  ]

QueryDSL

QueryDSL就是我们第二种查询方式中请求体的内容

它的格式如下:

如果针对于某个字段,那么它的结构如下:

  QUERY_NAME:   # 使用的功能
     FIELD_NAME:  #  功能参数
       ARGUMENT:VALUE,
       ARGUMENT:VALUE,...
         
   


GET bank/_search

  "query":   #  查询的字段
    "match_all": 
  ,
  "from": 0,  # 从第几条文档开始查
  "size": 5,
  "_source":["balance"],	#_source为要返回的字段
  "sort": [
    
      "account_number":   # 返回结果按哪个列排序
        "order": "desc"  # 降序
      
    
  ]

常用查询

match全文检索

GET bank/_search

  "query": 
    "match": 
      "account_number": "20"
    
  


#字符串
GET bank/_search

  "query": 
    "match": 
      "address": "kings"
    
  

match_phrase短语匹配

规则如下:

# 精准匹配, 不拆分字符串进行检索
# match_phrase:不拆分字符串进行检索
# 字段.keyword:必须全匹配上才检索成功
GET bank/_search

  "query": 
    "match_phrase": 
      "address": "mill road"
    
  


GET bank/_search

  "query": 
    "match": 
      "address.keyword": "990 Mill"  
    
  

query/bool/must复合查询
# must:必须达到must所列举的所有条件
GET bank/_search

   "query":
        "bool":   
             "must":[
              "match":"address":"mill",
              "match":"gender":"M"
             ]
         
    



# must_not:必须不匹配must_not所列举的所有条件。
GET bank/_search

  "query": 
    "bool": 
      "must": [
        
          "match": 
            "gender": "M"
          
        ,
        
          "match": 
            "address": "mill"
          
        
      ],
      "must_not": [
        
          "match": 
            "age": "38"
          
        
      ]
    
  


# should:应该达到should列举的条件,如果到达会增加相关文档的评分,并不会改变查询的结果。如果query中只有should且只有一种匹配规则,那么should的条件就会被作为默认匹配条件二区改变查询结果。
GET bank/_search

  "query": 
    "bool": 
      "must": [
        
          "match": 
            "gender": "M"
          
        ,
        
          "match": 
            "address": "mill"
          
        
      ],
      "must_not": [
        
          "match": 
            "age": "18"
          
        
      ],
      "should": [
        
          "match": 
            "lastname": "Wallace"
          
        
      ]
    
  

能够看到相关度越高,得分也越高。

query/filter【结果过滤】

  • must 贡献得分
  • should 贡献得分
  • must_not 不贡献得分
  • filter 不贡献得分

匹配的越多,得分越高!只有must和should影响相关性得分

GET bank/_search

  "query": 
    "bool": 
      "must": [
         "match": "address": "mill"  
      ],
      "filter":   
        "range": 
          "balance":   
            "gte": "10000",
            "lte": "20000"
          
        
      
    
  



#这条是不会显示结果的,结果都是0
GET bank/_search

  "query": 
    "bool": 
      "filter": 
        "range": 
          "balance": 
            "gte": "10000",
            "lte": "20000"
          
        
      
    
  


query/term

# 全文检索字段用match,其他非text字段匹配用term。
GET bank/_search

  "query": 
    "term": 
      "address": "mill Road"
    
  

aggs/agg1(聚合)

# 聚合查询就是查一点这个,查一点哪个,不是单一的查询
GET bank/_search

  "query": 
    "match": 
      "address": "Mill"
    
  ,
  "aggs": 
    "ageAgg": 
      "terms": 
        "field": "age",
        "size": 10
      
    ,
    "ageAvg": 
      "avg": 
        "field": "age"
      
    ,
    "balanceAvg": 
      "avg": 
        "field": "balance"
      
    
  ,
  "size": 0

nested对象聚合

GET articles/_search

  "size": 0, 
  "aggs": 
    "nested":  # 
      "nested":  #
        "path": "payment"
      ,
      "aggs": 
        "amount_avg": 
          "avg": 
            "field": "payment.amount"
          
        
      
    
  

Mapping字段映射

映射定义文档如何被存储和检索的

Mapping映射是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和索引的。比如:使用mapping来定义:

当然对于已经存在的字段进行映射的时候,我们不能进行更新。更新必须创建新的索引才行

# 创建索引的时候去指定

PUT /my_index

  "mappings": 
    "properties": 
      "age": 
        "type": "integer"
      ,
      "email": 
        "type": "keyword" 
      ,
      "name": 
        "type": "text"
      
    
  



更新索引

对于已经存在的字段映射,我们不能更新。更新必须创建新的索引,进行数据迁移。

添加新的字段映射`PUT /my_index/_mapping`
PUT /my_index/_mapping

  "properties": 
    "employee-id": 
      "type": "keyword",
      "index": false # 字段不能被检索。检索
    
  

数据迁移

POST _reindex

  "source": 
    "index": "bank",
    "type": "account"
  ,
  "dest": 
    "index": "newbank"
  


GET /newbank/_search

分词

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文件夹下

[root@localhost plugins]# pwd
/mydata/elasticsearch/plugins/

重启ES,测试分词器是否安装成功

GET _analyze

   "text":"我是中国人"

如果可以解析中文成功,证明安装成功

调整虚拟机内存为3G

自定义词库

上面ES虽然能识别中文了,但是也是一个一个字识别的,我们想让它按我们的规矩来的话,我们需要自定义一下

默认es是不支持一些新的词语,它的词库里组合是很不规律的

那么我们可以自定义扩展一下这个词库!怎么扩展呢?

安装nginx

将分词内容放到nginx服务器当中

随便启动一个Nginx,目的是单纯为了复制配置文件
docker run -p 80:80 --name nginx -d nginx:1.10

docker container cp nginx:/etc/nginx .
mv nginx/ conf
mv conf/ /mydata/nginx
docker rm -f nginx

结构如下:
[root@localhost mydata]# cd nginx/
[root@localhost nginx]# ls
conf
[root@localhost nginx]# cd conf/
[root@localhost conf]# ls
conf.d  fastcgi_params  html  koi-utf  koi-win  logs  mime.types  modules  nginx.conf  scgi_params  uwsgi_params  win-utf

docker run -p 80:80 --name nginx \\
-v /mydata/nginx/html:/usr/share/nginx/html \\
-v /mydata/nginx/logs:/var/log/nginx \\
-v /mydata/nginx/conf:/etc/nginx \\
-d nginx:1.10

mkdir /mydata/nginx/html/es
[root@localhost ~]# cat /mydata/nginx/html/es/fenci.txt 
彭于晏
杜兰特

修改ESIKAnalyzer.cfg.xml

[root@localhost config]# cat IKAnalyzer.cfg.xml 
CTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict"></entry>
	 <!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords"></entry>
	<!--用户可以在这里配置远程扩展字典 -->
	<entry key="remote_ext_dict">http://192.168.1.8/es/fenci.txt</entry> 
	<!--用户可以在这里配置远程扩展停止词字典-->
	<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

修改完成后,需要重启es容器,否则修改不生效。

docker restart elasticsearch

POST _analyze

   "analyzer": "ik_max_word", 
   "text":"我是彭于晏"

当然后面有新词的话,那就直接在es指定目录下继续添加新词即可

整合ElasticSearch

我们先来一个简单的写写试试

官方文档如下:
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-document-index.html

配置类

固定写法,其中builder = RestClient.builder(new HttpHost(“192.168.1.8”, 9200, “http”));如果有ES集群的话可以用来指定多个ES主机地址

@Configuration
public class EsConfig 
    public static final RequestOptions COMMON_OPTIONS以上是关于谷粒商城--整合Elasticsearch和商品的上架的主要内容,如果未能解决你的问题,请参考以下文章

谷粒商城高级篇商品服务 & 商品上架

谷粒商城ES调用(十九)

谷粒商城笔记+踩坑——上架商品spu到ES索引库

谷粒商城_06_JSR303校验+Elasticsearch

谷粒商城商品服务API

谷粒商城商品服务API