谷粒商城学习笔记,第五天:ES全文检索
一、基本概念
注:ES7和8以后就不再支持type了
1、Index索引
相当于MySQL中的Database
2、Type类型(ES8以后就不支持了)
相当于MySQL中的table
3、Document文档(JSON格式)
相当于MySQL中的数据
倒排索引:
正向索引:
当用户在主页上搜索关键词“华为手机”时,假设只存在正向索引(forward index),那么就需要扫描索引库中的所有文档,
找出所有包含关键词“华为手机”的文档,再根据打分模型进行打分,排出名次后呈现给用户。因为互联网上收录在搜索引擎中的文档的
数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。
倒排索引:
一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。我们首先将每个文档的拆分成单独的词,
创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。
得到倒排索引的结构如下:
关键词 | 文档ID |
关键词1 | 文档1,文档2 |
关键词2 | 文档2,文档7 |
从词的关键字,去找文档。
二、Docker安装ES
1、下载镜像文件
docker pull elasticsearch:7.4.2
docker pull kibana:7.4.2 ##可视化检索数据
2、安装
##现在本地创建挂在路径
mkdir -p /opt/software/mydata/elasticsearch/config
mkdir -p /opt/software/mydata/elasticsearch/data
mkdir -p /opt/software/mydata/elasticsearch/plugins
##设置读写权限
chmod -R 777 /opt/software/mydata/elasticsearch/
##配置文件:远程访问
echo "http.host: 0.0.0.0" >> /opt/software/mydata/elasticsearch/config/elasticsearch.yml
##安装ES
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xms128m" \
-v /opt/software/mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /opt/software/mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /opt/software/mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
##查看日志
docker logs elasticsearch
##安装Kibana
docker run --name kibana -p 5601:5601 \
-e ELASTICSEARCH_HOSTS=http://ES的地址:9200 \
-d kibana:7.4.2
验证
访问:http://你的服务器IP:9200 es
http://你的服务器IP:5601 kibana
如果是阿里的服务器,要先在安全组规则中放开9200 5601
9200作为Http协议,主要用于外部通讯
9300作为Tcp协议,jar之间就是通过tcp协议通讯,ES集群之间是通过9300进行通讯
三、初步检索
3.1、_cat
命令 | 作用 |
GET /_cat/nodes | 查看所有节点 |
GET /_cat/health | 查看ES健康状况 |
GET /_cat/master | 查看ES主节点 |
GET /_cat/indices | 查看所有索引:show databases |
3.2、新增数据:put/post
##put和post 都可以做新增和修改操作
##post 可以带ID也可以不带ID,第一次新增,第二次修改
##put 必须带ID,否则报错,同样第一次表新增,第二次表修改。[因为必须带ID,所有一般用来做修改]
##语法
POST http://IP:9200/index/type/id id可带可不带
{
name: "lee"
}
PUT http://IP:9200/index/type/id id必须带
{
name: "lee"
}
3.3、查询数据:GET&乐观锁
##语法
GET http://IP:9200/customer/external/1
结果:
{
"_index": "customer", ##索引
"_type": "external", ##类型
"_id": "1", ##doc的ID
"_version": 5, ##版本号,添加修改次数
"_seq_no": 5, ##并发控制字段,每次更新就会+1,用来做乐观锁
"_primary_term": 1, ##同上,主分片重新分配,如重启,就会变化,用来做乐观锁
"found": true, ##true表示查找到了
"_source": {
"name": "ren"
}
}
##乐观锁:为了防止并发修改
##我们在修改时可以做如下操作:
PUT http://IP:9200/customer/external/1?_seq_no=5&_primary_term=1
{
"name":"ren"
}
3.4、更新数据:PUT/POST
##覆盖更新:将原来的数据先删除,再新增
POST http://IP:9200/customer/external/1
{
"name":"lee",
"gender":"male",
"age":20
}
PUT http://IP:9200/customer/external/1
{
"name":"ren",
"gender":"male"
}
##局部更新:只修改列出来的字段
POST http://IP:9200/customer/external/1/_update
{
"doc":{
"name":"lee_ren",
}
}
3.5、删除数据和索引:DELETE
##删除数据
DELETE http://IP:9200/customer/external/1
##删除索引(没有删除type)
DELETE http://IP:9200/customer
3.6、批量导入数据:bulk
注意:独立操作,没有事务
##########语法:action表示POST PUT DELETE
{ action: { metadata }}
{ request body }
{ action: { metadata }}
{ request body }
#########批量新增
http://IP:9200/_bulk
{"create":{"_index":"haoke","_type":"user","_id":"aaa"}}
{"id":1001,"name":"name1","age":20,"sex":"男"}
{"create":{"_index":"haoke","_type":"user","_id":"bbb"}}
{"id":1002,"name":"name2","age":21,"sex":"女"}
{"create":{"_index":"haoke","_type":"user","_id":"ccc"}}
{"id":1003,"name":"name3","age":22,"sex":"女"}
或者:
{"index":{"_index":"haoke","_type":"user","_id":"aaa"}}
{"id":1001,"name":"name1","age":20,"sex":"男"}
{"index":{"_index":"haoke","_type":"user","_id":"bbb"}}
{"id":1002,"name":"name2","age":21,"sex":"女"}
{"index":{"_index":"haoke","_type":"user","_id":"ccc"}}
{"id":1003,"name":"name3","age":22,"sex":"女"}
#########批量删除
POST http://IP:9200/_bulk
{"delete":{"_index":"haoke","_type":"user","_id":"aaa"}}
{"delete":{"_index":"haoke","_type":"user","_id":"bbb"}}
{"delete":{"_index":"haoke","_type":"user","_id":"ccc"}}
########批量修改
POST http://IP:9200/_bulk
请求body:
{"index":{"_index":"haoke","_type":"user","_id":"aaa"}}
{"id":1001,"name":"name111","age":20,"sex":"男"}
{"index":{"_index":"haoke","_type":"user","_id":"bbb"}}
{"id":1002,"name":"name222","age":21,"sex":"女"}
{"index":{"_index":"haoke","_type":"user","_id":"ccc"}}
{"id":1003,"name":"name333","age":22,"sex":"女"}
#########批量局部修改
POST http://IP:9200/_bulk
请求body:
{"update":{"_index":"haoke","_type":"user","_id":"aaa"}}
{"doc":{"name":"name111a"}}
{"update":{"_index":"haoke","_type":"user","_id":"bbb"}}
{"doc":{"name":"name222b"}}
{"update":{"_index":"haoke","_type":"user","_id":"ccc"}}
{"doc":{"name":"name333c"}}
elasticsearch自带的批量 测试数据:
https://download.elastic.co/demos/kibana/gettingstarted/8.x/accounts.zip
四、检索进阶
第一种检索方式:
##将所有的请求参数,放在URI路径中
GET http://IP:9200/bank/_search?q=*&sort=account_number:asc
[q=*表示查询所有,sort表示排序字段为account_number 升序,默认from为0 size为10]
第二种检索方式:Query DSL
##将所有的请求参数,放在body体中
GET http://IP:9200/bank/_search
{
"query": {
"match_all": {}
},
"sort": {
"account_number": "asc"
}
}
[query表示查询 match_all表示查询所有,sort表示排序,默认from为0 size为10]
4.1、Query DSL基本语法
GET http://IP:9200/bank/_search
{
"query": {
"match_all": {}
},
"sort": {
"account_number": "asc"
}
"from": 0,
"size": 5,
"_source": ["account_number","balance"]
}
[query表示查询 match_all表示查询所有,sort表示排序,from表示从第几个数据开始查]
[size表示查询记录数,_source表示要返回的字段]
4.2、Query DSL:match
##match 精确匹配,
##非String类型的字段 相当于 == 号
##String类型的字段 相当于like,(分词后的==精确匹配)
GET http://IP:9200/bank/_search
{
"query": {
"match": {
"address": "mill"
}
}
}
##match加上keyword,是精确匹配 address必须是990 Mill Road,大小写也要区分
##注意macth+keyword同match_phrase的区别:
##macth+keyword必须是一模一样的 == ,match_phrase是包含就可以 like,且不区分大小写
GET http://IP:9200/bank/_search
{
"query": {
"match": {
"address.keyword": "990 Mill Road"
}
}
}
4.3、Query DSL:match_phrase
##match_phrase 短语匹配,
##match会将 "mill road"分成两个词"mill"和"road"进行匹配,
##match_phrase 则会将"mill road"作为一个完整的词进行匹配(大小写 和 中间多余的空格不会影响查询结果)
GET http://IP:9200/bank/_search
{
"query": {
"match_phrase": {
"address": "mill road"
}
}
}
4.4、Query DSL:multi_match
##multi_match多字段匹配
##相当于 username == "aa" or nickname == "aa"
GET http://IP:9200/bank/_search
{
"query": {
"multi_match": {
"query":"mill movico",
"fields":["address","city"]
}
}
}
##es会将mill和movico分词,结果类似于 address == mill or address == movico or city == mill or city == movico
4.5、Query DSL:bool
##bool复合查询
##bool组合几个查询,他们是and的关系
##must是必须满足,must_not是必须不满足,should是满足也可以不满足也可以,但满足后排序会靠前些
##must和should会提高相关得分score,must_not不会影响得分(filter也不会影响得分)
GET /bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address":"mill"
}
},
{
"match": {
"gender": "m"
}
}
],
"must_not": [
{
"match": {
"age": "28"
}
}
],
"should": [
{
"match": {
"city": "blackgum"
}
}
]
}
}
}
4.6、Query DSL:filter
##filter结果过滤
##filter类似must,但不影响得分score结果
##must和should会影响相关结果的score,但filter不会
##range类似between
GET /bank/_search
{
"query": {
"bool": {
"filter": {
"range": {
"age": {
"gte": 10,
"lte": 30
}
}
}
}
}
}
4.7、Query DSL:term
##term等同于match,但term只能用于精确的非text文本格式的查找(不能分词)
##match可以进行text的分词后的查找的
##约定:非text字段用term,text字段用match
##注意例如:city:"hebei"是String可以用term查找
##address:"hebei handan"是text需要用match查找
GET /bank/_search
{
"query": {
"term": {
"address": "mill"
}
}
}
五、聚合:aggregations
##聚合:从数据中分组和提取数据
##类似于SQL中的group by\count\avg等
##聚合类型 term avg平均 min最小 max最大 sum综合
##注意:文本和字符串形式的聚合使用keyword聚合
##语法:
"aggs": {
"aggs name聚合名称": {
"aggs type聚合类型": {
"field": "聚合的字段",
"size": "想要多少个结果"
},
....子聚合
},
...其他聚合
}
##eg
##1、搜索address中包含mill的所有人的年龄分布以及平均年龄和平均薪资
##[size:0 显示搜索数据0条,即不显示搜索结果]
## 其他聚合
GET /bank/_search
{
"query": {
"match": {
"address": "mill"
}
},
"aggs": {
"ageAggs": {
"terms": {
"field": "age",
"size": 10
}
},
"ageAvgAggs":{
"avg": {
"field": "age"
}
},
"balanceAvgAggs":{
"avg": {
"field": "balance"
}
}
},
"size":0
}
##2、按照年龄聚合,并查处每个年龄段的平均薪资
##子聚合
GET /bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAggs": {
"terms": {
"field": "age",
"size": 10
},
"aggs": {
"balanceAvg": {
"avg": {
"field": "balance"
}
}
}
}
},
"size": 0
}
##3、查处所有年龄分布,并且这些年龄段中的平均薪资以及年龄段中M男性的平均薪资和F女性的平均薪资
##[gender是文本形式的,无法进行聚合,用gender.keyword聚合]
GET /bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAggs": {
"terms": {
"field": "age",
"size": 10
},
"aggs": {
"ageBalanceAvg": {
"avg": {
"field": "balance"
}
},
"genderAggs": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"genderBalanceAvg": {
"avg": {
"field": "balance"
}
}
}
}
}
}
},
"size": 0
}
六、映射:Mapping
##映射:mapping类似SQL中定义字段的数据类型
##1、查看映射
GET /bank/_mapping
##2、创建映射
PUT /myindex
{
"mappings": {
"properties": {
"name":{"type": "text"},
"age":{"type": "integer"},
"email":{"type": "keyword"},
"birthday":{"type": "date"}
}
}
}
##3、在已有的mapping上,添加新的映射字段
PUT /myindex/_mapping
{
"properties":{
"employee_id":{"type":"long"}
}
}
##4、修改:已存在映射的已存在字段是不能修改的,只能添加新的字段
##如果非要想 修改已有的映射,需要重新创建映射mapping,然后再迁移数据
##如:就有的bank索引是带有type的,现在我们创建新的映射并去掉bank
##创建新的mapping
PUT /newbank
{
"mappings": {
"properties": {
"account_number": {
"type": "long"
},
"address": {
"type": "text"
},
"age": {
"type": "integer"
},
"balance": {
"type": "long"
},
"city": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"email": {
"type": "keyword"
},
"employer": {
"type": "text"
},
"firstname": {
"type": "text"
},
"gender": {
"type": "keyword"
},
"lastname": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"state": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
##数据迁移:将原来bank中的数据迁移到newbank中
##数据迁移
POST _reindex
{
"source": {
"index": "bank",
"type": "account"
},
"dest": {
"index": "newbank"
}
}
##注意上面是ES旧版本带有type类型的数据迁移,新版本的数据不带type,迁移语法如下:
POST _reindex
{
"source": {
"index": "bank"
},
"dest": {
"index": "newbank"
}
}
七、分词
7.1、分词
一个tokenizer(分词器)接收一个字符流,将之分割为独立的tokens(词元,通常是独立的单词),然后输出tokens流。
##分词器
POST _analyze
{
"analyzer": "standard",
"text": "Besides traveling and volunteering, something else that’s great for when you don’t give a hoot is to read."
}
7.2、IK分词器
下载地址 GitHub medcl/elasticsearch-analysis-ik
##安装IK分词器: 下载到elasticsearch下的plugins目录下
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
##解压
unzip elasticsearch-analysis-ik-7.4.2.zip
##修改权限
chmod -R 777 ik/
##检测是否安装好了
docker exec -it fcd2b /bin/bash
cd bin
elasticsearch-plugin list
##重启es
docker restart fcd2
测试:
##中文分词
POST _analyze
{
"analyzer": "ik_smart",
"text": "我是中国河北人"
}
##中文分词
POST _analyze
{
"analyzer": "ik_max_word",
"text": "我是中国河北人"
}
7.3、自定义扩展词库
7.3.1、Docker安装Nginx
##下载nginx
docker pull nginx:1.10
##启动运行
docker run -p 80:80 --name nginx -d nginx:1.10
##复制docker容器中nginx的配置文件到本地
cd /opt/software/mydata/
mkdir nginx
cd nginx
mkdir html
mkdir logs
docker container cp nginx:/etc/nginx .
##安装新的nginx
docker run -p 80:80 --name nginx \
-v /opt/software/mydata/nginx/html:/usr/share/nginx/html \
-v /opt/software/mydata/nginx/logs:/var/log/nginx \
-v /opt/software/mydata/nginx/conf:/etc/nginx \
-d nginx:1.10
测试:
##在/opt/software/mydata/nginx/html下创建index.html
##内容:
<h1>welcome to nginx</h1>
##浏览器访问
http://IP:80/
7.3.2、扩展词库
创建词库
##在/opt/software/mydata/nginx/html下创建一个文件:es/fenci.txt
##内容:
乔碧萝
徐静蕾
##浏览器访问:
http://IP/es/fenci.txt
es关联词库
##配置es plugins目录下IK分词器conf目录下的配置文件IKAnalyzer.cfg.xml
<!--用户可以在这里配置远程扩展字典 -->
<entry key="remote_ext_dict">http://你的IP/es/fenci.txt</entry>
##重启es
docker restart
##测试
POST _analyze
{
"analyzer": "ik_smart",
"text": "我不认识乔碧萝殿下"
}