目录
一、ES简介
1. 数据库查询的问题
select * from 商品信息表 where name like '%华为Mate60%'
可以搜索到:华为手机Mate60, 华为手机Mate60 Pro, 白色 华为手机Mate60
但是不能搜索:华为 手机Mate60
假如我们有一个电商系统,需要查询商品。以前一直是直接从MySql数据库里用SQL语句模糊查询,例如:
select * from tb_product where pname like '%华为手机%'
华为手机Mate 50 Pro 华为畅享手机
但是这种查询检索数据的方式,有很大的问题:
-
查询性能很低
MySql可以通过索引的方式来提高查询的效率,但是像上边这种模糊查询以%开头,会导致索引失效,实际上执行的是全表扫描,效率非常低。
-
查询结果不准确
我们应当查询到与“手机”、“华为”等词条相关的所有商品,而不是 只包含“华为手机”的商品
而这些问题,都可以使用ElasticSearch轻易的解决掉:把数据存储到ElasticSearch里,由ElasticSearch提供高效的文档检索能力
2. ES简介
1 ElasticSearch简介
ElasticSearch,简称ES。ES是一款非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到需要的内容。如百度搜索、京东商品搜索、打车软件中搜附近的车辆等等。
ES结合kibana、Logstash、Beats(也就是ELK技术栈,Elastic Stack),被广泛的用户在日志数据分析、实时监控等领域。ES是ELK的核心,负责存储、搜索、分析数据。
2 ElasticSearch发展
-
Doug Cutting于1999年研发了一个Java语言的搜索引擎类库(函数库):Lucene,目前已是Apache的顶级项目。
官网地址:Apache Lucene - Welcome to Apache Lucene
Lucene优势:易扩展、高性能(基于倒排索引)
Lucene缺点:只限于Java语言、学习曲线陡峭、不支持水平扩展
-
2004年Shay Banon基于Lucene开发了Compass,随后于2010年重写了Compass,取名为ElasticSearch。
官网地址:Elasticsearch 平台 — 大规模查找实时答案 | Elastic
相比于Lucene,ElasticSearch具备更多优势:
-
支持分布式,可水平扩展
-
提供RESTful接口(HTTP方式的请求),任何编程语言都可以使用。
-
-
到目前为止,ElasticSearch已经成为主流的搜索引擎
3. 倒排索引【面试】
1 正向索引
说明
传统RDBMS(例如MySql)采用的是正向索引,即:根据表的主键字段构建索引,在查询时要扫描到每个索引值对应的数据,从中筛选出符合查询条件的记录。
这样的检索方式构建索引简单,但是不适合复杂的文档检索。复杂的文档检索时,索引会失效,变成全表扫描
示例
以商品表搜索为例,商品表tb_product如下:
要搜索包含“手机”的商品,执行SQL:select * from tb_product where title like '%华为手机%'
MySql会扫描所有的数据记录,逐条判断 “title”的值是否包含“手机”,直到全表扫描,过滤出所有符合要求的结果
-
先找每条数据
-
再判断每条数据是否符合要求
2 倒排索引
说明
ElasticSearch采用的是倒排索引,即:以字或词为关键字构建索引,保存每个关键字所在的记录。当需要查询时,根据词条匹配查询条件,直接找到关联的记录。
倒排索引的建立和维护都比较复杂,但是在查询时可以和查询关键字关联的所有结果,并快速响应。
示例
以商品表搜索为例,商品表略
构建倒排索引
当搜索“华为手机”时:
-
把搜索条件进行分词,得到两个词条:“华为”, “手机”
-
根据词条,去倒排索引里查询相关的文档id,得到:
-
“华为”:2, 3
-
“手机”:1, 2
-
-
根据文档id,找到对应的文档数据,得到:id为1、2、3的文档
4. ES和MySql
传统的RDBMS和ElasticSearch都可以增删改查,并且各有所长:
-
MySQL:擅长事务类操作,可以确保数据的一致性和安全性
-
ES:擅长海量数据的搜索、分析、计算
在实际开发中,通常是结合使用
-
MySQL负责写操作
-
ES负责搜索操作
5. 小结
ES是什么?是一个开源的搜索引擎,可以快速从海量数据里查找目标数据
ES为什么快?其中一个原因是,ES使用了倒排索引,避免了全表扫描,所以速度快
-
ES会对所有的数据,进行分词构建倒排表和倒排索引。 是词条对应文档的id集合
-
当我们要搜索数据的时候,ES会先对搜索词进行分词,拿分词后的多个词条,直接去倒排索引里找到关联的文档id集合;从而找到关联的文档数据
ES和MySQL的对比:
-
共同点:都具备增删改查的能力
-
不同点:
-
MySQL擅长增删改 写操作。因为MySQL有事务,可以保证数据的一致性和完整性
-
ES擅长查询搜索。因为ES使用了倒排索引,可以快速从海量数据里查找目标数据
-
-
实际开发中的应用:
-
如果要增删改数据,找MySQL
-
再把MySQL里的数据同步给ES
-
我们再从ES里搜索查询数据
-
二、安装ES
1. 方式1:使用docker安装
1 准备工作
首先把《es7.4.0.tar》和《kibana7.4.0.tar》上传到CentOS的/root目录里
然后执行以下命令:
2 创建ElasticSearch容器
3 给ElasticSearch配置ik分词器
可以之前去输入拉取镜像的命令,这里是直接加载准备好的包
把ik分词器插件文件夹上传到 CentOS的/data/elasticsearch/plubins
文件夹里,重启ElasticSearch容器
重启es
4 创建Kibana容器
注意:一定要把命令里的ip地址,修改成ElasticSearch的访问ip
5 访问测试
打开浏览器访问:http://192.168.200.130:5601 如果能正常打开,说明已经完全启动成功了
注意:ElasticSearch和Kibana的启动,都需要花费一定时间,如果浏览器页面打不开,就耐心等待一会
2. 方式2:本机直接安装
1. 安装ES
目前我们安装的是ES的7.4.0版本,需要JDK8及以上
准备工作:Cpolar关闭或者卸载,因为它占用了9200端口。这个端口是ES使用的
关闭Cpolar的方式:
-
打开“服务”窗口,找到Cpolar Service
-
在服务上右键停止;
-
在服务上右键-属性-启动类型,修改为“手动”;确定并关闭
-
打开浏览器,输入地址 http://localhost:9200
-
如果显示出来了一个登录页面,就按 ctrl + shift + delete,清除浏览器缓存
-
再刷新页面,保证页面是不能访问的。(不能访问,说明Cpolar已经成功关闭了)
-
1 解压
把《elasticsearch-7.4.0-windows-x86_64.zip》解压到一个不含中文、空格、特殊字符的目录里
2 配置
1) 配置存储路径
打开config/elasticsearch.yml文件,设置索引数据的存储路径,和日志的存储路径
2) 配置虚拟机参数
3 启动
进入es的bin目录,直接双击 elasticsearch.bat即可启动
es服务要占用两个端口:
-
9200:rest访问接口,我们稍后要通过这个端口连接es、操作es
-
9300:用于es集群间通信的接口
4 验证
在浏览器上直接输入地址 http://localhost:9200/, 如果看到以下界面,说明es启动成功了
2. 安装Kibana
Kibana是一个ES索引库数据统计工具,可以利用ES的聚合功能,生成各种图表,如柱形图,线状图,饼图等。
而且还提供了操作ES索引数据的控制台,并且提供了一定的API提示,非常有利于我们学习ES的语法。
1 安装nodeJs
Kibana依赖于nodeJs,需要在windows下先安装Node.js,然后才能安装Kibana。
双击nodejs的安装包,按照提示一步步安装即可
安装成功后,打开cmd 输入:node -v
,如果能看到版本号,说明node安装成功
2 安装Kibana
docker pull kibana:7.4.0 拉取kibana镜像,可以直接拉取
1) 解压
把《kibana-7.4.0-windows-x86_64.zip》解压到不含中文、空格、特殊字符的目录里
2) 配置
修改kibana的config/kibana.yml,配置es的地址
3) 启动
进入到kibana的bin目录,双击 kibana.bat 启动。启动稍微有些慢,耐心等待一会
4) 访问
打开浏览器输入 http://localhost:5601/
3. 安装IK分词器
1 ES标准分词器的问题
ES自带的分词器叫“Standard Analyzer”,它对中文的支持非常不友好,会将一个词语的每个字拆分成一个词条,和中文的语法是完全不符的。
例如,一个词语“黑马程序员”,按中文语法习惯可以拆分成“黑马”,“程序员”,“程序”等词条。但是标准分词器分拆分成“黑”,“马”,“程”,“序”,“员”
示例如下:
2 安装IK分词器
为了解决中文词语拆分的问题,可以安装一个IK分词器。IK分词器是一款适合于中文分词习惯的、优秀的中文分词器,具有60万字/秒的高速处理能力。
它支持两种粒度的拆分:
-
ik_smart:做粗粒度的拆分
-
ik_max_word:将文本做细粒度的拆分,拆分出尽可能多的词条(建议用这种)
1) 解压
把分词器《elasticsearch-analysis-ik-7.4.0.zip》解压到es的plugins目录下,重命名为“ik”
2) 重启
-
重启es
-
重启kibana
3) 测试
在浏览器的kibana开发工具界面,输入并执行
4. 安装过程中可能出现的问题
1 JDK环境变量问题
现象:启动ES时,黑窗口一闪而过
可能的原因:ElasticSearch7.4,要求使用jdk8或者jdk11。如果JDK版本不正确,启动es会报错(可能是JDK环境变量有问题)
验证方式:打开cmd,执行java -version
,看一下JDK版本;再执行javac -version
,看一下版本号
2 端口占用问题
现象:启动ES时,黑窗口运行着运行着就关了
可能的原因:苍穹外卖里内网穿透软件cpolar,占用的是9200端口,会导致使用es出问题
解决方式:
-
方式一:关掉cpolar服务或者卸载cpolar,然后重启电脑,再启动es,就没有端口冲突了。es会占用9200端口
关闭cpolar的方式:Windows+R,运行 services.msc,找到 Cpolar 服务,右键停止;然后右键属性,启动方式修改为手动
-
方式二:不管它,es发现9200被占用,它会自动改用其它端口。要看一下启动es的控制台,看它的端口是多少 (一般是9201)。
然后Kibana要配置连接ES时的地址,配置成 http://localhost:9201
3 操作系统权限问题
你的电脑当前用户权限不足,会导致启动es和Kibana会报错 “operate not promitted...”。解决方法是:
es文件夹和Kibana文件夹都需要设置:
-
在文件夹上右键-属性-安全-编辑-添加
-
在弹出的容器里,添加当前计算机用户名(可以在cmd里使用
net user
查看)
勾选中这个用户,点击下边的“完全控制”,再确定
3. ik分词器
ES标准分词器的问题
ES自带的分词器叫“Standard Analyzer”,它对中文的支持非常不友好,会将一个词语的每个字拆分成一个词条,和中文的语法是完全不符的。
例如,一个词语“黑马程序员”,按中文语法习惯可以拆分成“黑马”,“程序员”,“程序”等词条。但是标准分词器分拆分成“黑”,“马”,“程”,“序”,“员”
示例如下:
IK分词器的使用示例
在浏览器的kibana开发工具界面,输入并执行
4. 小结
ES于客户端连接
安装ElasticSearch
-
准备工作
先把镜像文件上传到Linux里。加载镜像文件:docker load -i 文件名
准备文件夹和配置文件,稍后要用于跟es容器进行挂载
-
创建es容器。
-
把ik分词器上传到Linux里
-
创建Kibana容器
分词器:
-
es自带标准分词器:standard分词器,对中文分词效果不好
-
ik分词器:对中文分词效果好。分词粒度有:
ik_smart:智能分词
ik_max_word:尽可能拆分出更多的词条
三、操作索引
ES我们通常不用java去创建索引库,一般直接在kibana直接几行直接搞定
1. ES的核心概念
2. 操作索引库
1 索引库映射mapping(类似MySql的表结构)
说明
映射mapping:目的是为了给索引的每个Field设置类型(字符串,数字,日期,对象,数组……)
在MySql里,我们在插入一条记录之前,需要提前设置好表里每个每个字段的类型。
而类似的,在ES里,我们需要给每个索引库设置映射信息
参考:Put mapping API | Elasticsearch Guide [7.4] | Elastic
映射类型
每个字段field要设置的 常见的maping属性有:
-
type:字段的类型,常见的简单类型有
-
text:可分词的文本字符串。如果是text,需要设置
analyzer
分词器 -
keyword:不分词的文本字符串。某些字符串一旦分词就失去意义了,例如品牌、国家、ip地址、url地址等
-
byte,short,integer,long,double,float等数值
-
boolean
-
date日期。ES可以把日期格式化为字符串存储,但是为了节省空间,建议使用long存储毫秒值
-
object对象。如果是object,需要设置
properties
-
-
index:是否创建索引,默认true。如果字段值不参与搜索,要设置为false。比如图片url
-
analyzer:使用哪种分词器。text类型时需要设置,其它类型的字段不需要设置
-
properties:字段的子字段。如果字段是object时,需要设置子字段
示例
有如下文档数据,分析该索引库的mapping映射
2 索引库的CURD
1 语法
-
创建索引库
-
查看索引库
查看所有索引库:
GET /_cat/indices
查看某个索引库:
GET /索引库名
-
修改索引库
注意:es里不能修改已有字段,可以给索引库添加字段
因为创建好索引库后,es会构建倒排索引,这个过程是比较消耗性能的。一旦索引库的某个字段被修改,可能会导致原本的倒排索引失效,影响太大,所以es不能修改索引库中已有的字段。
删除索引库:DELETE /索引库名
2 示例
要求:创建一个索引库,用于存储黑马讲师的信息
3 课堂演示
3 练习
要求1:创建一个索引库itcast,用于存储商品信息。要有以下字段:
-
商品id id
-
商品名称 title
-
商品价格 price
-
所属品牌 brand
-
商品库存量 quantity
-
商品图片url地址 images
要求2:查看索引库itcast的信息
要求3:给索引库itcast增加一个字段“商品分类 category”
要求4:删除索引库itcast
3. 小结
ES的几个概念:
-
Index:索引。相当于MySQL里的表
-
Document:文档。相当于MySQL表里的一行数据。只不过是json格式
-
Field:字段。相当于MySQL表里的字段
-
Mapping:映射。相当于MySQL表的结构,包括有哪些字段、字段的类型、约束等等
索引的映射结构:创建索引时,每个字段需要设置
-
type类型:
-
字符串有2种
text:如果字段值是字符串,并且要分词,就使用text。如果是text,就必须给这个字段设置analyzer
keyword:如果字段值是字符串,并且不分词,就使用keyword
-
数字类型:byte, short, integer, long, double, float等等
-
布尔类型:boolean
-
日期类型:date
-
对象类型:object
-
-
index是否参与搜索:如果某个字段的值,不需要根据这个字段搜索数据,就给字段设置index值为false
-
analyzer:分词器。如果字段类型是text的时候,必须设置分词器。
standard 标准分词器
ik_smart ik智能分词
ik_max_word ik细粒度的分词
操作ES索引的语法:
四、操作文档
本章节学习目标:
- 掌握文档的CURD
1. 语法
参考:Document APIs | Elasticsearch Guide [7.4] | Elastic
-
新增与修改文档
如果文档id不存在:是新增文档
如果文档id已存在:是修改文档,会直接覆盖掉原文档
-
查看文档:
GET /索引库名/_doc/id
-
修改文档:增量修改。
在原文档基础上进行修改某些字段的值
删除文档:DELETE /索引库名/_doc/id值
2. 示例
3. 课堂演示
4. 动态映射
思考
如果新增文档的结构,与mapping结构不一致,会出现什么结果
例如
查询索引的映射:GET /itcast/_mapping
看到结果
说明
插入文档时,es会检查文档中的字段是否有对应的mapping,如果没有,则按照默认的mapping规则来创建索引。
如果默认的mapping不符合你的要求,一定要自己设置字段的mapping
5. 小结
五、RestAPI
1. 准备工作
官方文档:Compatibility | Java REST Client [7.4] | Elastic
1 创建project导入依赖
导入es的客户端依赖坐标
注意:es客户端依赖的版本号,必须要和es的版本号一致
2 创建实体类
商品Product,对应es里的product索引
3 RestAPI的使用说明
要想操作es,需要按照如下步骤:
-
创建RestHighLevelClient对象
-
使用RestHighLevelClient操作es
-
关闭RestHighLevelClient
2. 操作索引【了解】
1 API说明
RestHighLevelClient对象提供了操作索引库的API方法,常用的有:
-
创建:
restClient.indices().create(CreateIndexRequest request, RequestOptions options)
-
删除:
restClient.indices().delete(DeleteIndexRequest request, RequestOptions options)
-
是否存在:
restClient.indices().exists(GetIndexRequest request, RequestOptions options)
以上方法的参数:
-
CreateIndexRequest
:创建索引库的请求对象 -
DeleteIndexRequest
:删除索引库的请求对象 -
GetIndexRequest
:查询索引库的请求对象 -
RequestOptions
:创建索引库的请求选项,使用固定值RequestOptions.DEFAULT
即可
注意事项:
-
使用RestAPI创建索引库非常繁琐,建议在Kibana里使用DSL语句执行操作,而不是用Java代码创建索引库
2 使用示例
索引库分析
要把一条商品信息存储到es,我们要先分析一下对应的索引库及映射该如何编写:
-
id: 商品id,可以使用long类型
-
title:商品名称,使用text类型,要分词并构建倒排索引进行搜索
-
category:商品分类,分类是整体,不进行分词,使用keyword
-
brand:品牌,和分类类似,不进行分词,使用keyword
-
price:价格,double类型
-
images:图片地址,用来展示的字段,不需要构建索引,不参与搜索;index设置为false,使用keyword类型
编辑DSL命令如下:
操作示例
使用RestHighLevelClient操作的示例代码如下:
package com.itheima;
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class Demo01Index {
private RestHighLevelClient client;
/**
* 创建索引库
*/
@Test
public void testCreateIndex() throws IOException {
String json = "{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\": {\n" +
" \"type\": \"long\"\n" +
" },\n" +
" \"title\": {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\"\n" +
" },\n" +
" \"category\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"brand\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"price\": {\n" +
" \"type\": \"double\"\n" +
" },\n" +
" \"images\": {\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
CreateIndexRequest request = new CreateIndexRequest("itheima")
.source(json, XContentType.JSON);
client.indices().create(request, RequestOptions.DEFAULT);
}
/**
* 删除索引库
*/
@Test
public void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("itheima");
client.indices().delete(request, RequestOptions.DEFAULT);
}
/**
* 索引库是否存在
*/
@Test
public void testExistsIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("itheima");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
@BeforeEach
public void init(){
client = new RestHighLevelClient(RestClient.builder(
new HttpHost("localhost", 9200)
));
}
@AfterEach
public void destroy() throws IOException {
client.close();
}
}
3. 操作文档【重点】
1 API说明
使用Java操作文档,和在kibana里编写DSl非常类似。只是由Java代码来执行相同的操作
RestHighLevelClient提供了操作文档的API如下:
-
新增与修改:
client.index(IndexRequest request, RequestOptions options)
-
查看:
client.get(GetRequest request, RequestOptions options)
-
删除:
client.delete(DeleteRequest request, RequestOptions options)
以上方法的参数:
-
IndexRequest
:新增与修改文档的请求对象 -
GetRequest
:查询文档的请求对象 -
DeleteRequest
:删除文档的请求对象 -
RequestOptions
:请求选项参数,使用固定值RequestOptions.DEFAULT
即可
2 使用示例
package com.itheima;
import com.alibaba.fastjson.JSON;
import com.itheima.pojo.Product;
import org.apache.http.HttpHost;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class Demo02Document {
RestHighLevelClient client = null;
/**
* 保存文档
* 如果id不存在,是新增
* 如果id已存在,是修改
*/
@Test
public void testSaveDocument() throws IOException {
Product product = new Product(1L, "华为 Mate40 pro", "手机数码", "华为", 5200D, "http://images.huawei.com/pro40.jpg");
String productJson = JSON.toJSONString(product);
//1. 准备Request对象,设置索引库名称
IndexRequest indexRequest = new IndexRequest("product")
//设置文档id
.id(product.getId().toString())
//设置文档数据
.source(productJson, XContentType.JSON);
//2. 发送请求
client.index(indexRequest, RequestOptions.DEFAULT);
}
/**
* 查看文档
*/
@Test
public void testFindDocument() throws IOException {
//1. 准备Request
GetRequest getRequest = new GetRequest("product", "1");
//2. 发送请求
GetResponse response = client.get(getRequest, RequestOptions.DEFAULT);
//3. 处理响应结果
String docJson = response.getSourceAsString();
Product product = JSON.parseObject(docJson, Product.class);
System.out.println(product);
}
/**
* 删除文档
*/
@Test
public void testDeleteDocument() throws IOException {
//1. 准备Request
DeleteRequest deleteRequest = new DeleteRequest("product", "1");
//2. 发送请求
client.delete(deleteRequest, RequestOptions.DEFAULT);
}
@BeforeEach
public void init() {
client = new RestHighLevelClient(RestClient.builder(
new HttpHost("localhost", 9200)
));
}
@AfterEach
public void destroy() throws IOException {
client.close();
}
}
4. 批量操作文档
1 说明
批量操作的核心在于将多个普通请求封装在一个批量请求中,一次性发送到服务器,下面仅仅演示批量新增(其他自己尝试)
操作的步骤:
-
准备BulkRequest对象
-
把每个文档数据,封装到一个IndexRequest对象里。把IndexRequest添加到BulkRequest对象中
-
使用client.bulk(bulkRequest对象, RequestOptions.DEFAULT)
2 示例
/**
* 批量操作文档
*/
@Test
public void testBulkDocument() throws IOException {
//0. 模拟数据
List<Product> list = new ArrayList<Product>();
list.add(new Product(11L, "小米手机", "手机数码", "小米", 3299.00, "http://image.huawei.com/13123.jpg"));
list.add(new Product(12L, "锤子手机", "手机数码", "锤子", 3699.00, "http://image.huawei.com/13123.jpg"));
list.add(new Product(13L, "联想手机", "手机数码", "联想", 4499.00, "http://image.huawei.com/13123.jpg"));
list.add(new Product(14L, "红米手机", "手机数码", "小米", 4299.00, "http://image.huawei.com/13123.jpg"));
list.add(new Product(15L, "iPhone X", "手机数码", "苹果", 8299.00, "http://image.huawei.com/13123.jpg"));
list.add(new Product(16L, "MacBook pro", "电脑", "苹果", 14999.00, "http://image.huawei.com/13123.jpg"));
list.add(new Product(17L, "AirPods", "手机数码", "苹果", 4299.00, "http://image.huawei.com/13123.jpg"));
list.add(new Product(18L, "外星人", "电脑", "DELL", 21299.00, "http://image.huawei.com/13123.jpg"));
list.add(new Product(19L, "联想小新", "电脑", "联想", 6299.00, "http://image.huawei.com/13123.jpg"));
//1. 创建Request
BulkRequest bulkRequest = new BulkRequest();
//2. 添加数据
for (Product product : list) {
//2.1 把一个Product对象,封装到一个IndexRequest对象里
IndexRequest indexRequest = new IndexRequest("product").id(product.getId().toString());
indexRequest.source(JSON.toJSONString(product), XContentType.JSON);
//2.2 把一个IndexRequest对象添加到BulkRequest对象里
bulkRequest.add(indexRequest);
}
//3. 发送请求
client.bulk(bulkRequest, RequestOptions.DEFAULT);
}
5. 小结
使用Java操作ES,准备工作:
-
添加依赖坐标:关键是elasticsearch-rest-high-level-client。我们提供的工程里已经有了,不需要额外再添加
-
修改配置文件:没有 跟es相关的配置,但是为了防止启动连接数据库不存在,先执行SQL脚本
-
把客户端连接对象放到IoC容器里。把以下代码加到引导类里,或者加到一个配置类里
4. 创建单元测试类,添加@SpringBootTest,再注入RestHighLevelClient对象
操作索引的API
//1. 创建索引
CreateIndexRequest request = new CreateIndexRequest("索引名")
.source("json字符串", XContentType.JSON);
esClient.indices().create(request, RequestOptions.DEFAULT);
//2. 删除索引
DeleteIndexRequest request = new DeleteIndexRequest("索引名");
esClient.indices().delete(request, RequestOptions.DEFAULT);
//3. 判断索引是否存在
GetIndexRequest request = new GetIndexRequest("索引名")
boolean exists = esClient.indices().exists(request, RequestOptions.DEFAULT);
操作文档的API
//1. 新增或修改
IndexRequest request = new IndexReuqest("索引名")
.id("文档唯一标识")
.source("文档json字符串", XContentType.JSON);
esClient.index(request, RequestOptions.DEFAULT);
//2. 查询
GetRequset request = new GetRequest("索引名").id("文档唯一标识");
GetResponse response = esClient.get(request, RequestOptions.DEFAULT);
String docJson = response.getSourceAsString();
//3. 删除
DeleteRequest request = new DeleteRequest("索引名").id("文档唯一标识");
esClient.delete(request, RequestOptions.DEFAULT);
//4. 批量操作:通常用于批量新增,也可以用于批量删除等写操作
BulkRequest bulkRequest = new BulkRequest("索引名");
for(Xxx xx: list){
//准备IndexRequest对象
IndexRequest request = new IndexRequest()
.id(xx.getId().toString())
.source(JSON.toJSONString(xx), XContentType.JSON);
//把IndexRequest添加到BulkRequest对象里
bulkRequest.add(request);
}
//最后一次性把BulkRequest里的数据存储到es里
esClient.bulk(bulkRequest, RequestOption.DEFAULT);
六、练习
需求
执行SQL脚本《资料/tb_hotel.sql》,其中有tb_hotel表,存储了酒店的数据
利用BulkRequest批量将数据库数据导入到索引库中。
步骤
-
根据数据库表结构,在es里创建对应的索引库
-
使用Java程序读取MySQL数据,再使用RestHighLevelClient存储到es的索引库里
实现
1. 创建索引库
使用Kibana执行DSL语句创建索引库,DSL语句如下:
2. 准备环境
创建一个新的project,执行以下步骤:
导入依赖
配置文件
引导类
实体类Hotel
对应MySQL的tb_hotel表
package com.itheima.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("tb_hotel")
public class Hotel {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String longitude;
private String latitude;
private String pic;
}
实体类HotelDoc
对应ES的文档数据
package com.itheima.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
public HotelDoc(Hotel hotel) {
this.id = hotel.getId();
this.name = hotel.getName();
this.address = hotel.getAddress();
this.price = hotel.getPrice();
this.score = hotel.getScore();
this.brand = hotel.getBrand();
this.city = hotel.getCity();
this.starName = hotel.getStarName();
this.business = hotel.getBusiness();
this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
this.pic = hotel.getPic();
}
}
HotelMapper
public interface HotelMapper extends BaseMapper<Hotel> {
}
3. 批量插入数据
package com.itheima;
import com.alibaba.fastjson.JSON;
import com.itheima.mapper.HotelMapper;
import com.itheima.pojo.Hotel;
import com.itheima.pojo.HotelDoc;
import org.apache.http.HttpHost;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.List;
@SpringBootTest
public class HotelTest {
@Autowired
private HotelMapper hotelMapper;
@Test
public void test() throws IOException {
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200)));
//1. 读取数据库里所有的酒店数据
List<Hotel> list = hotelMapper.selectList(null);
//2. 循环酒店列表,把每个酒店数据添加到BulkRequest对象里
BulkRequest bulkRequest = new BulkRequest();
for (Hotel hotel : list) {
//把每个Hotel对象转换成对应ES的HotelDoc对象
HotelDoc doc = new HotelDoc(hotel);
//创建IndexRequest对象
IndexRequest request = new IndexRequest("hotel").id(doc.getId().toString()).source(JSON.toJSONString(doc), XContentType.JSON);
bulkRequest.add(request);
}
//3. 执行批量插入
client.bulk(bulkRequest, RequestOptions.DEFAULT);
//4. 关闭连接
client.close();
}
}
总结
大总结
ES是什么?是一个开源的搜索引擎,可以从海量数据里快速找到目标数据
ES为什么快?其中有一个原因就是使用了倒排索引
-
正向索引:找到一条数据记录,判断是否符合条件。可能会出现全表扫描
-
倒排索引:直接根据搜索条件,定位到符合条件的文档数据。不会有全表扫描,性能更高
ES和MySQL(关系型数据库)的对比
-
共同点:都可以增删改查数据
-
不同点:
MySQL或其它关系型数据库:擅长事务型的操作,增删改,因为有严谨的事务保证数据的一致性和完整性
ES搜索:擅长查询检索,因为ES有倒排索引,更适合于从海量数据里快速查询检索数据。特别是文本字符串的模糊匹配
-
实际使用时:两者配合使用
如果需要增删改数据,要操作MySQL
然后把MySQL里的数据同步到ES里
然后再从ES里查询检索数据
ES相关的几个概念:
-
Index:索引。相当于MySQL里的表
-
Mapping:映射。相当于MySQL里的表结构。即表里有什么字段、字段是什么类型、字段有什么其它限制约束等等
-
Field:字段。相当于MySQL表里的字段
-
Document:文档。是json格式的,相当于MySQL里的一行数据记录
-
DSL:用于操作ES的命令语法,相当于操作关系型数据库的SQL语句
DSL命令操作索引
DSL命令操作文档
Java操作索引
-
先添加依赖坐标:elasticsearch-rest-high-level-client
-
把RestHighLevelClient对象放到IoC容器里:在配置类里或者在引导类里添加
操作索引的Api
Java操作文档