1.ElasticSearch集群的搭建:
1.1克隆 docker-base: es
1.2设置虚拟机内存:右键点es虚拟机 — 设置 — 内存设置成2G
1.3设置ip:
#修改固定ip
./ip-static
ip: 192.168.64.181
#重新查看ip
ifconfig
1.4c导入镜像:
docker load -i es-img.gz
1.5重启docker系统服务:
systemctl restart docker
1.6设置一个系统的底层参数,重启服务器:
echo 'vm.max_map_count=262144' >>/etc/sysctl.conf
#重启服务器
shutdown -r now
1.7准备虚拟网络和挂载目录
# 创建虚拟网络
docker network create es-net
# node1 的挂载目录
mkdir -p -m 777 /var/lib/es/node1/plugins
mkdir -p -m 777 /var/lib/es/node1/data
# node2 的挂载目录
mkdir -p -m 777 /var/lib/es/node2/plugins
mkdir -p -m 777 /var/lib/es/node2/data
# node3 的挂载目录
mkdir -p -m 777 /var/lib/es/node3/plugins
mkdir -p -m 777 /var/lib/es/node3/data
1.8启动 Elasticsearch 集群(三个节点组成的集群–分别启动三个节点集群)
节点一:
- node1
docker run -d \
--name=node1 \
--restart=always \
--net es-net \
-p 9200:9200 \
-p 9300:9300 \
-v /var/lib/es/node1/plugins:/usr/share/elasticsearch/plugins \
-v /var/lib/es/node1/data:/usr/share/elasticsearch/data \
-e node.name=node1 \
-e node.master=true \
-e network.host=node1 \
-e discovery.seed_hosts=node1,node2,node3 \
-e cluster.initial_master_nodes=node1 \
-e cluster.name=es-cluster \
-e "ES_JAVA_OPTS=-Xms256m -Xmx256m" \
elasticsearch:7.9.3
节点二:
- node2
docker run -d \
--name=node2 \
--restart=always \
--net es-net \
-p 9201:9200 \
-p 9301:9300 \
-v /var/lib/es/node2/plugins:/usr/share/elasticsearch/plugins \
-v /var/lib/es/node2/data:/usr/share/elasticsearch/data \
-e node.name=node2 \
-e node.master=true \
-e network.host=node2 \
-e discovery.seed_hosts=node1,node2,node3 \
-e cluster.initial_master_nodes=node1 \
-e cluster.name=es-cluster \
-e "ES_JAVA_OPTS=-Xms256m -Xmx256m" \
elasticsearch:7.9.3
节点三:
- node3
docker run -d \
--name=node3 \
--restart=always \
--net es-net \
-p 9202:9200 \
-p 9302:9300 \
-v /var/lib/es/node3/plugins:/usr/share/elasticsearch/plugins \
-v /var/lib/es/node3/data:/usr/share/elasticsearch/data \
-e node.name=node3 \
-e node.master=true \
-e network.host=node3 \
-e discovery.seed_hosts=node1,node2,node3 \
-e cluster.initial_master_nodes=node1 \
-e cluster.name=es-cluster \
-e "ES_JAVA_OPTS=-Xms256m -Xmx256m" \
elasticsearch:7.9.3
1.9查看启动结果结果
http://192.168.64.181:9200
http://192.168.64.181:9200/_cat/nodes
10.启动ElasticSearch时的参数说明
--name #es节点的名称
--net es-net #指定es节点挂载的虚拟网络(三个es节点挂载到同一条虚拟网络)
-p 9200 #客户端与es服务之间通信的端口
-p 9300 #es服务节点之间相互通信的端口(服务器与服务)
-v /var/lib/es/node3/plugins:/usr/share/elasticsearch/plugins \ #挂载到容器的路径
-v /var/lib/es/node3/data:/usr/share/elasticsearch/data \ #挂载到容器的路径
-e node.name=node3 \ #ES的环境变量--------节点名
-e node.master=true \ #ES的环境变量-----是否可以选举为主节点
-e network.host=node3 \ #ES的环境变量-----当前节点的地址
-e discovery.seed_hosts=node1,node2,node3 \ #服务发现(没有注册中心,直接通过节点列表做服务发现),集群中其他节点的地址列表,通过地址列表发现其它节点
-e cluster.initial_master_nodes=node1 \ #ES的环境变量-----候选的主节点地址列表
-e cluster.name=es-cluster \ #ES的环境变量------集群名
-e "ES_JAVA_OPTS=-Xms256m -Xmx256m" \ #ES的环境变量---------java虚拟机参数
2.ElasticSearch的客户端工具
ES的集群搭建成功,需要安装es的客户端工具,连接ES集群
chrome浏览器插件:elasticsearch-head
2.1安装ElasticSearch-Head:
2.1.1在 elasticsearch-head 项目仓库中下载 chrome 浏览器插件
https://github.com/mobz/elasticsearch-head/raw/master/crx/es-head.crx
2.1.2将文件后缀改为 zip
2.1.3解压缩
2.1.4在 chrome 浏览器中选择“更多工具”–“扩展程序”
2.1.5在“扩展程序”中确认开启了“开发者模式”
2.1.6点击“加载已解压的扩展程序”
2.1.7选择前面解压的插件目录
2.1.8在浏览器中点击 elasticsearch-head 插件打开 head 界面,并连接 http://192.168.64.181:9200/
2.2 IK分词器(不是ElasticSearch自带的,需要另外安装)
ES的搜索—倒排索引:
倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
就是通过搜索关键词,得到所有包括这些关键词的文档
IK分词器安装(版本号必须与ElasticSearch的版本保持一致)
安装 ik 分词器:
1.从 ik 分词器项目仓库中下载 ik 分词器安装包,下载的版本需要与 Elasticsearch 版本匹配:
https://github.com/medcl/elasticsearch-analysis-ik
或者可以访问 gitee 镜像仓库:
https://gitee.com/mirrors/elasticsearch-analysis-ik
2.下载 elasticsearch-analysis-ik-7.9.3.zip 复制到 /root/ 目录下
3.在三个节点上安装 ik 分词器
#进入root目录
cd ~/
# 复制 ik 分词器到三个 es 容器
docker cp elasticsearch-analysis-ik-7.9.3.zip node1:/root/
docker cp elasticsearch-analysis-ik-7.9.3.zip node2:/root/
docker cp elasticsearch-analysis-ik-7.9.3.zip node3:/root/
# 在 node1 中安装 ik 分词器
docker exec -it node1 elasticsearch-plugin install file:///root/elasticsearch-analysis-ik-7.9.3.zip
# 在 node2 中安装 ik 分词器
docker exec -it node2 elasticsearch-plugin install file:///root/elasticsearch-analysis-ik-7.9.3.zip
# 在 node3 中安装 ik 分词器
docker exec -it node3 elasticsearch-plugin install file:///root/elasticsearch-analysis-ik-7.9.3.zip
# 重启三个 es 容器
docker restart node1 node2 node3
4.查看安装结果
在浏览器中访问 http://192.168.64.181:9200/_cat/plugins
5.如果IK词后期使用出现问题,插件不可用,可以卸载后重新安装
docker exec -it node1 elasticsearch-plugin remove analysis-ik
docker exec -it node2 elasticsearch-plugin remove analysis-ik
docker exec -it node3 elasticsearch-plugin remove analysis-ik
ik分词测试
ik分词器提供两种分词器: ik_max_word 和 ik_smart
ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query;
测试:
1.打开谷歌浏览器,打开ElasticSearch-Head插件
2.连接ElasticSearch集群,连接 http://192.168.64.181:9200/
3.点复合查询
4.向 http://192.168.64.181:9200/_analyze 路径提交 POST 请求,并在协议体中提交 Json 数据
{
"analyzer":"ik_smart",
"text":"中华人民共和国国歌"
}
5.查看分词结果
ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询。
测试:
1.打开谷歌浏览器,打开ElasticSearch-Head插件
2.连接ElasticSearch集群,连接 http://192.168.64.181:9200/
3.点复合查询
4.向 http://192.168.64.181:9200/_analyze 路径提交 POST 请求,并在协议体中提交 Json 数据
{
"analyzer":"ik_smart",
"text":"中华人民共和国国歌"
}
5.查看分词结果
2.3 Kibana客户端
ElasticSearch-Head客户端操作ES集群输入没有提示,不方便,启Kibana客户端操作更加方便
1.启动Kibana容器
docker run \
-d \
--name kibana \
--net es-net \
-p 5601:5601 \
-e ELASTICSEARCH_HOSTS='["http://node1:9200","http://node2:9200","http://node3:9200"]' \
--restart=always \
kibana:7.9.3
2.启动后,浏览器访问 Kibana,进入 Dev Tools:
http://192.168.64.181:5601/
3.ElasticSearch的索引,分片和副本(分布式搜索引擎)
- 索引
Elasticsearch索引用来存储我们要搜索的数据,以倒排索引结构进行存储。
当索引中存储了大量数据时,大量的磁盘io操作会降低整体搜索新能,这时需要对数据进行分片存储。
- 索引分片
在一个索引中存储大量数据会造成性能下降,这时可以对数据进行分片存储。
每个节点上都创建一个索引分片,把数据分散存放到多个节点的索引分片上,减少每个分片的数据量来提高io性能
每个分片都是一个独立的索引,数据分散存放在多个分片中,也就是说,每个分片中存储的都是不同的数据。搜索时会同时搜索多个分片,并将搜索结果进行汇总。
如果一个节点宕机分片不可用,则会造成部分数据无法搜索,为了解决这一问题,可以对分片创建多个副本来解决。
- 副本
对分片创建多个副本,那么即使一个节点宕机,其他节点中的副本分片还可以继续工作,不会造成数据不可用
分片的工作机制:
主分片的数据会复制到副本分片
搜索时,以负载均衡的方式工作,提高处理能力
主分片宕机时,其中一个副本分片会自动提升为主分片
3.1创建索引
创建一个名为 products 的索引,用来存储商品数据。
分片和副本参数说明:
number_of_shards:分片数量,默认值是 5
number_of_replicas:副本数量,默认值是 1
我们有三个节点,在每个节点上都创建一个分片。每个分片在另两个节点上各创建一个副本。
#创建索引:
#向es服务器提交PUT请求,请求地址:http://192.168.64.181:9200/products
PUT products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
}
}
创建索引成功后,可以在ElasticSearch-Head上查看
用索引名称过滤,查看 products 索引
3.2映射(数据结构)
类似于数据库表结构,索引数据也被分为多个数据字段,并且需要设置数据类型和其他属性。
映射,是对索引中字段结构的定义和描述。
常用类型:
数字类型:
byte、short、integer、long
float、double
unsigned_long
字符串类型:
text : 会进行分词
keyword : 不会进行分词,适用于email、主机地址、邮编等
日期和时间类型:
date
创建映射
在 products 索引中创建映射。
分词器设置:
analyzer:在索引中添加文档时,text类型通过指定的分词器分词后,再插入倒排索引
search_analyzer:使用关键词检索时,使用指定的分词器对关键词进行分词
查询时,关键词优先使用 search_analyzer 设置的分词器,如果 search_analyzer 不存在则使用 analyzer 分词器。
#关键词优先使用 search_analyzer 设置的分词器,如果 search_analyzer 不存在则使用 analyzer 分词器。
"title": {
"type": "text",
"analyzer": "ik_max_word", #存储文档时使用的分词器
"search_analyzer": "ik_smart" #对搜索内容分词,如果不设置就是用上面的analyzer
},
创建products索引:
# 定义mapping,数据结构
PUT /products/_mapping
{
"properties": {
"id": {
"type": "long"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"category": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
},
"price": {
"type": "float"
},
"city": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
},
"barcode": {
"type": "keyword"
}
}
}
查看映射
GET /products/_mapping
3.3ElasticSearch添加文档
# 添加文档 指定索引id
PUT /products/_doc/10033
{
"id":"10033",
"title":"SONOS PLAY:5(gen2) 新一代PLAY:5无线智能音响系统 WiFi音箱家庭,潮酷数码会场",
"category":"潮酷数码会场",
"price":"3980.01",
"city":"上海",
"barcode":"527848718459"
}
PUT /products/_doc/10034
{
"id":"10034",
"title":"天猫魔盒 M13网络电视机顶盒 高清电视盒子wifi 64位硬盘播放器",
"category":"潮酷数码会场",
"price":"398.00",
"city":"浙江杭州",
"barcode":"522994634119"
}
PUT /products/_doc/10035
{
"id":"10035",
"title":"BOSE SoundSport耳塞式运动耳机 重低音入耳式防脱降噪音乐耳机",
"category":"潮酷数码会场",
"price":"860.00",
"city":"浙江杭州",
"barcode":"526558749068"
}
PUT /products/_doc/10036
{
"id":"10036",
"title":"【送支架】Beats studio Wireless 2.0无线蓝牙录音师头戴式耳机",
"category":"潮酷数码会场",
"price":"2889.00",
"city":"上海",
"barcode":"37147009748"
}
PUT /products/_doc/10037
{
"id":"10037",
"title":"SONOS PLAY:1无线智能音响系统 美国原创WiFi连接 家庭桌面音箱",
"category":"潮酷数码会场",
"price":"1580.01",
"city":"上海",
"barcode":"527783392239"
}
# 添加文档 没指定索引,自动生成索引id
POST /products/_doc
{
"id":"10027",
"title":"vivo X9前置双摄全网通4G美颜自拍超薄智能手机大屏vivox9",
"category":"手机会场",
"price":"2798.00",
"city":"广东东莞",
"barcode":"541396973568"
}
索引的id可以自动生成,也可以指定,一般应索引id和文档内容id保持一致,使用更方便
查看添加的所有文档
ElasticSearch-Head -> 数据浏览 ->products
访问文档
#访问10037的文档(原始文档)
GET /products/_doc/10037
#查看指定文档字段的分词结果:
#查看文档10035字段title的分词结果
GET /products/_doc/10035/_termvectors?fields=title
#查看文档10035字段title的分词结果
GET /products/_doc/10035/_termvectors?fields=category
#查看文档10035字段title的分词结果
GET /products/_doc/10035/_termvectors?fields=city
3.4ElasticSearch修改文档
底层索引数据无法修改,修改数据实际上是先删除再重新添加。
两种修改方式:
PUT:对文档进行完整的替换
POST:可以修改一部分字段
3.4.1.修改价格字段的值(PUT的方式):
# 修改文档 - 替换(全部替换--不修改的字段也要给出,否则为空)
#PUT请求的提求方式的路径:PUT /products/_doc/10037
PUT /products/_doc/10037
{
"id":"10037",
"title":"SONOS PLAY:1无线智能音响系统 美国原创WiFi连接 家庭桌面音箱",
"category":"潮酷数码会场",
"price":"9999.99",
"city":"上海",
"barcode":"527783392239"
}
查看文档
GET /products/_doc/10037
3.4.2.修改价格字段的值(POST的方式):
# 修改文档 - 更新部分字段
#POST请求的提求方式的路径:POST /products/_update/10037
POST /products/_update/10037
{
"doc": {
"price":"8888.88",
"city":"深圳"
}
}
查看文档
GET /products/_doc/10037
3.5ElasticSearch删除文档
3.5.1.ES中删除文档是delete请求:
DELETE /products/_doc/10037
3.5.2.清空products索引下的所有文档
POST /products/_delete_by_query
{
"query": {
"match_all": {}
}
}
3.5.3.删除索引
# 删除 products 索引
DELETE /products
4.ElasticSearch搜索案例
4.1.创建索引和映射
PUT /pditems
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"brand": {
"type": "text",
"analyzer": "ik_smart"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"sell_point": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"price": {
"type": "float"
},
"image": {
"type": "keyword"
},
"cid": {
"type": "long"
},
"status": {
"type": "byte"
},
"created": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"updated": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
导入测试数据
为了测试搜索功能,我们首先导入测试数据,3160条商品数据,数据样例如下:
(每两条json组成一条数据,第一条为索引数据,第二条为文档内容)
{ "index": {"_index": "pditems", "_id": "536563"}}
{ "id":"536563",
"brand":"联想",
"title":"联想(Lenovo)小新Air13 Pro 13.3英寸14.8mm超轻薄笔记本电脑",
"sell_point":"清仓!仅北京,武汉仓有货!",
"price":"6688.0",
"barcode":"",
"image":"/images/server/images/portal/air13/little4.jpg",
"cid":"163",
"status":"1",
"created":"2015-03-08 21:33:18",
"updated":"2015-04-11 20:38:38"
}
下载测试数据
https://download.csdn.net/download/weixin_38305440/13129113
将压缩文件中的 pditems.json 上传到服务器
在服务器上(VM虚拟机),进入 pditems.json 所在的文件夹,执行批量数据导入命令:
curl -XPOST 'localhost:9200/pditems/_bulk' \
-H 'Content-Type:application/json' \
--data-binary @pditems.json
查看导入数据:
查看添加的所有文档
ElasticSearch-Head -> 数据浏览 -> pditems
4.2.搜索文档
4.2.1搜索所有数据
# 搜索 pditems 索引中全部数据(size字段指定返回多少条数据)
POST /pditems/_search
{
"query": {
"match_all": {}
}
, "size": 20
}
4.2.2关键词搜索
# 查询 pditems 索引中title中包含"电脑"的商品
POST pditems/_search
{
"query": {
"match": {
"title": "电脑"
}
}
}
4.2.3搜索结果过滤器
# 价格大于2000,并且title中包含"电脑"的商品
#bool值过滤器,里面包含多个布尔值条件:"must"为必须满足的条件,"filter"为过滤器
POST pditems/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "电脑"
}
}
],
"filter": [
{
"range": {
"price": {
"gte": 2000
}
}
}
]
}
}
}
4.2.4搜索结果高亮显示
#标题title和卖点sell_point都匹配关键词手机,并高亮显示
# "multi_match"多字段匹配
# highlight高亮显示,pre_tags前置标签,post_tags后置标签(搜索结:手机关键词前后加标签)
#fields 哪一个属性的关键词高亮显示
POST pditems/_search
{
"query": {
"multi_match": {
"query": "手机",
"fields": ["title","sell_point"]
}
},
"highlight": {
"pre_tags": "<i class='highlight'>",
"post_tags": "</i>",
"fields": {
"title": {
"pre_tags": "<em class='highlight1'>",
"post_tags": "</em>"
},
"sell_point": {}
}
}
}
5. Spring Data Elasticsearch - 增删改查API
为了测试方便,准备一些简单的数据进行测试:
在 Elasticsearch 中存储学生数据,并对学生数据进行搜索测试。
数据表结构
学号 | 姓名 | 性别 | 出生日期 |
---|---|---|---|
27 | 张三 | 男 | 2021-12-4 |
案例测试以下数据操作:
创建 students 索引和映射
C - 创建学生数据
R - 访问学生数据
U - 修改学生数据
D - 删除学生数据
使用 Repository 和 Criteria 搜索学生数据
ngram分词器(匹配中文姓名比较适用)
在ElasticSearch中如果要使用ngram分词器,需要创建索引时添加配置
#官网可查,直接复制使用
"index.max_ngram_diff":30,
"analysis": {
"analyzer": {
"ngram_analyzer": {
"tokenizer": "ngram_tokenizer"
}
},
"tokenizer": {
"ngram_tokenizer": {
"type": "ngram",
"min_gram": 1,
"max_gram": 30,
"token_chars": [
"letter",
"digit"
]
}
}
}
5.1 创建students索引
在 Elasticsearch 中创建 students 索引并使用ngram分词器(创建索引时配置)
在开始运行测试之前,在 Elasticsearch 中先创建 students 索引:
PUT /students
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2,
"index.max_ngram_diff":30,
"analysis": {
"analyzer": {
"ngram_analyzer": {
"tokenizer": "ngram_tokenizer"
}
},
"tokenizer": {
"ngram_tokenizer": {
"type": "ngram",
"min_gram": 1,
"max_gram": 30,
"token_chars": [
"letter",
"digit"
]
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text",
"analyzer": "ngram_analyzer"
},
"gender": {
"type": "keyword"
},
"birthDate": {
"type": "date",
"format": "yyyy-MM-dd"
}
}
}
}
5.2 搭建springBoot项目
5.2.1 POM文件–>添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>es</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>es</name>
<description>es</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.2.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>cn.tedu.es.EsApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
5.2.2 配置文件yml(添加ElasticSearch集群)
spring:
elasticsearch:
rest:
uris:
- http://192.168.64.181:9200
- http://192.168.64.181:9201
- http://192.168.64.181:9202
#查看ElasticSearch服务器通讯日志
logging:
level:
tracer : trace
5.2.3 添加启动类
5.2.4 新建entity类(POJO)
实体类加注解与ElasticSearch索引进行绑定(类似mybatisPlus)
package cn.tedu.es.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
/**
* es客户端api,可以根据注解在服务器端自动创建索引.
* 一般都是自己手动创建索引,而不应该依赖于api自动创建
* @Document(indexName = "students",shards = 3,replicas = 2)
* 指定索引名为students,分片数量为3,副本数为2
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "students",shards = 3,replicas = 2)
public class Student{
//学号
@Id
private Long id;
@Field("name") //es服务器索引中的字段字与属性同名,可以省略,自动匹配
private String name;
private Character gender;
private String birthDate;
}
5.2.5 编写ElasticSearch动态代理接口(底层自动创建动态代理实现类,与es服务交互)
package cn.tedu.es.es;
import cn.tedu.es.entity.Student;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* spring data 定义的数据访问规范接口Repository,
* Repository的子接口,都不需要自己编写实现代码,
* 都是通过动态代理自动生成代码
*
* ElasticsearchRepository<Student,Long> : Students对应的实体类,Long为索引id的数据类型
*/
public interface StudentRepository extends ElasticsearchRepository<Student,Long> {
}
5.2.6 编写测试类–>测试接口与es服务器的数据通信
ElasticSearch的增删改查(CRUD)操作:
package cn.tedu.es;
import cn.tedu.es.entity.Student;
import cn.tedu.es.es.StudentRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Optional;
@SpringBootTest
public class Test1 {
@Autowired
private StudentRepository r;
/**
* 添加数据
* 调用sava()方法
*/
@Test
public void test1(){
r.save(new Student(9525L,"唐伯虎",'男',"2022-04-12"));
r.save(new Student(9526L,"唐伯猫",'女',"2022-01-12"));
r.save(new Student(9528L,"唐伯狗",'男',"2022-01-30"));
r.save(new Student(9529L,"祝枝山",'男',"2022-04-12"));
r.save(new Student(9530L,"华夫人",'男',"2022-04-12"));
r.save(new Student(9531L,"华安",'男',"2022-04-12"));
r.save(new Student(9532L,"石榴姐",'女',"2022-04-12"));
r.save(new Student(9533L,"小强",'男',"2022-04-12"));
r.save(new Student(9534L,"旺财",'男',"2022-04-12"));
}
/**
* 修改数据
* 调用sava()方法
*/
@Test
public void test2(){
r.save(new Student(9530L,"如花",'女',"2022-04-12"));
}
/**
* 查询方法
* findById():查询单条数据
* findAll():查询所有数据
*/
@Test
public void test3(){
/*
java.util.Optional
用来防止出现空指针异常
*/
Optional<Student> op = r.findById(9527L);
if(op.isPresent()){//判断Optional中是否存在Student对象
Student stu = op.get();
System.out.println(stu);
}
System.out.println("========================");
Iterable<Student> it = r.findAll();
for (Student s : it) {
System.out.println(s);
}
}
/**
* 删除方法
*/
@Test
public void test4(){
r.deleteById(9534L);
}
}
5.2.7 ElasticSearch搜索方法
修改ElasticSearch动态代理接口(5.2.5定义的ElasticSearch接口)
添加了两个方法(使用方法进行搜索)
package cn.tedu.es.es;
import cn.tedu.es.entity.Student;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
/**
* 方法得符合ElasticSearch的命名规范,底层就会自动调用
*/
public interface StudentRepository extends ElasticsearchRepository<Student,Long> {
//在name字段搜索关键词
List<Student> findByName(String key);
//在name和birthDate两个字段搜索
List<Student> findByNameOrBirthDate(String name,String birthDate);
/**
* 分页查询
* 分页工具:
* 1.Pageable:封装向服务器提交的分页参数(页码从0形如)
* - 页号
* - 每页大小
*
* 2.Page(可选):封装从服务器返回的一页数据
* - 查询的当前页数据
* - 分页的属性数据:当前页号,
* 每页大小,
* 是否有上一页下一页,
* 共多少页,
* 有多少条数据(数据量是多少)
*/
//org.springframework.data.domain.Pageable
Page<Student> findByNameOrBirthDate(String name, String birthDate, Pageable pageable);
}
5.2.8 编写测试类—>测试ElasticSearch服务器的搜索
/**
* 搜索方法
* findByName()方法
*/
@Test
public void test5(){
List<Student> list = r.findByName("唐");
for (Student s : list) {
System.out.println(s);
}
}
/**
* 搜索方法
* findByNameOrBirthDate()方法
*/
@Test
public void test6(){
List<Student> list = r.findByNameOrBirthDate("唐","2020-09-18");
for (Student s : list) {
System.out.println(s);
}
/**
* 测试分页搜索
* findByNameOrBirthDate()方法
*/
@Test
public void test7(){
Pageable pageable = PageRequest.of(0,2);//第一个参数,页号 第二参数
Page<Student> page = r.findByNameOrBirthDate("唐", "2020-09-27", pageable);
for (Student s : page) {
System.out.println(s);
}
System.out.println("------------------");
System.out.println("是否有上一页"+page.hasPrevious());
System.out.println("是否有下一页"+page.hasNext());
System.out.println("总页数"+page.getTotalPages());
}
}
5.2.9 ElasticSearch高亮显示
修改ElasticSearch动态代理接口
在List<Student> findByName(String key);方法上加注解.配置高亮显示
1.findByName方法上加高亮显示的注解
2.findByName方法的返回值改为List<SearchHit<Student>>对象
package cn.tedu.es.es;
import cn.tedu.es.entity.Student;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface StudentRepository extends ElasticsearchRepository<Student,Long> {
//在name字段搜索关键词
/*
1. 用注解配置
- 对哪些字段做高亮显示
- 关键词前后添加的标签
2. 返回的数据:(一个List包含多个SearchHit对象,
一个SearchHit对象里面又包含Student对象的原始数据,和高亮的name数据)
- List
- SearchHit
- Student 原始数据
- 高亮的name
- SearchHit
- Student 原始数据
- 高亮的name
- SearchHit
- Student 原始数据
- 高亮的name
*/
@Highlight(
fields = {
@HighlightField(name = "name")
},
parameters = @HighlightParameters(
preTags = "<em>",
postTags = "</em>"
)
)
List<SearchHit<Student>> findByName(String key);
}
编写测试方法—>测试高亮显示结果
/**
* 搜索结果高亮显示
*
* 用注解配置
* - 对哪些字段做高亮显示
* - 关键词前后添加的标签
*
*
* 返回的数据:(一个List包含多个SearchHit对象,
* 一个SearchHit对象里面又包含Student对象的原始数据,和高亮的name数据)
* - List
* - SearchHit
* - Student 原始数据
* - 高亮的name:{"<em>","唐","</em>","伯虎"} //{"唐伯虎"}
* - SearchHit
* - Student 原始数据
* - 高亮的name:{"<em>","唐","</em>","伯虎"} //{"唐伯虎"}
* - SearchHit
* - Student 原始数据
* - 高亮的name:{"<em>","唐","</em>","伯虎"} //{"唐伯虎"}
*/
@Test
public void test8(){
List<SearchHit<Student>> list = r.findByName("唐");
for (SearchHit<Student> sh : list) {
Student stu = sh.getContent();//Student的原始数据,不包含高亮显示的结果
//{"<em>","唐","</em>","伯虎"} //{"唐伯虎"} 得到的结果需要重新拼接
List<String> hlName = sh.getHighlightField("name");
//重新拼接高亮字段,然后放入学生对象 ,替换原始的name
stu.setName(pinJie(hlName));
}
//处理完后,再重新显示
for(SearchHit<Student> sh : list){
System.out.println(sh.getContent());
}
}
private String pinJie(List<String> hlName) {
StringBuilder s = new StringBuilder();
for (String name:hlName){
s.append(name);
}
return s.toString();
}