目录
1 Elasticsearch 介绍
Elaticsearch简称为es,是⼀个开源的可扩展的分布式全⽂检索引擎服务器,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es使⽤Java开发并使⽤Lucene作为其核⼼来实现索引和搜索的功能,它通过简单的RestfulAPI和javaAPI来隐藏Lucene的复杂性,从⽽让全⽂搜索变得简单。
Elasticsearch官⽹:https://www.elastic.co/cn/products/elasticsearch
使⽤场景
- 分布式的搜索引擎
分布式:Elasticsearch⾃动将海量数据分散到多台服务器上去存储和检索
搜索:百度、⾕歌,站内搜索
- 全⽂检索
提供模糊搜索等⾃动度很⾼的查询⽅式,并进⾏相关性排名,⾼亮等功能
- 数据分析引擎(分组聚合)
电商⽹站,最近⼀周笔记本电脑这种商品销量排名top10的商家有哪些?新闻⽹站,最近1个⽉访问量排名top3的新闻板块是哪些
- 对海量数据进⾏近实时的处理
海量数据的处理:因为是分布式架构,Elasticsearch可以采⽤⼤量的服务器去存储和检索数据,⾃然⽽然就可以实现海量数据的处理
近实时:Elasticsearch可以实现秒级别的数据搜索和分析
相关概念
索引库[index]-----------------------------------Database 数据库
类型[type]----------------------------------Table 数据表
⽂档[Document]--------------------------Row ⾏
字段[Field]-------------------------Column 列
映射[mapping]-------------------表结构
详细说明
2 安装与启动
windows版安装包下载地址:https:// www.elastic.co/cn/downloads/elasticsearch
下载的安装包是解压缩就能使⽤的zip⽂件,解压缩完毕后会得到如下⽂件
- bin⽬录:包含所有的可执⾏命令
- config⽬录:包含ES服务器使⽤的配置⽂件
- jdk⽬录:此⽬录中包含了⼀个完整的jdk⼯具包,版本17,当ES升级时,使⽤最新版本的jdk确保不会出现版本⽀持性不⾜的问题
- lib⽬录:包含ES运⾏的依赖jar⽂件
- logs⽬录:包含ES运⾏后产⽣的所有⽇志⽂件
- modules⽬录:包含ES软件中所有的功能模块,也是⼀个⼀个的jar包。和jar⽬录不同,jar⽬录是ES运⾏期间依赖的jar包,modules是ES软件⾃⼰的功能jar包
- plugins⽬录:包含ES软件安装的插件,默认为空
启动服务器
双击bin目录下的elasticsearch.bat⽂件即可启动ES服务器,默认服务端⼝9200。通过浏览器
访问http://localhost:9200看到如下信息视为ES服务器正常启动
8.1及以上版本开启安全验证,访问后会在控制台出现如下问题
[WARN ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [DESKTOP-E5MROJA] received plaintext http traffic on an https channel, closing connection Netty4HttpChannel{localAddress=/[0:0:0:0:0:0:0:1]:9200, remoteAddress=/[0:0:0:0:0:0:0:1]:62537}
解决方法:1.可输入https://127.0.0.1:9200/访问并输入用户名和密码
2.修改config/elasticsearch.yml文件
找到:
xpack.security.enabled: true
xpack.security.http.ssl:
enabled: true
keystore.path: certs/http.p12修改为false
并在文件末尾添加以下两句
http.cors.enabled: true
http.cors.allow-origin: "*"
3 ES基本使⽤
在ES中我们要先创建索引,这个索引的功能⼜点类似于数据库的表,然后将数据添
加到倒排索引中,添加的数据称为⽂档。所以要进⾏ES的操作要先创建索引,再添
加⽂档,这样才能进⾏后续的查询操作。
要操作ES可以通过Rest⻛格的请求来进⾏,也就是说发送⼀个请求就可以执⾏
⼀个操作。⽐如新建索引,删除索引这些操作都可以使⽤发送请求的形式来进⾏。
创建索引
使用postman发送put请求,products是索引名称(body里不能有任何数据否则报错)
http://localhost:9200/products
发送请求后,看到如下信息即索引创建成功
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "products"
}
重复创建已经存在的索引会出现错误信息,reason属性中描述错误原因
查询索引
使用postman发送get请求,products是索引名称
http://localhost:9200/products
查询索引得到索引相关信息,如下
如果查询了不存在的索引,会返回错误信息
删除索引
使用postman发送delete请求,products是索引名称
删除所有后,给出删除结果。如果重复删除,会给出错误信息
创建索引并指定分词器
前⾯创建的索引是未指定分词器的,可以在创建索引时添加请求参数,设置分词器。⽬前国内较为流⾏的分词器是IK分词器,使⽤前先在下对应的分词器,然后使⽤。IK分词器下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
分词器下载后解压到ES安装⽬录的plugins⽬录中即可,安装分词器后需要重新启动ES服务器。使⽤IK分词器创建索引格式:PUT请求 http://localhost:9200/products
请求参数如下(注意是json格式的参数)
{
"mappings":{ #定义mappings属性,替换创建索引时对应的mappings属性
"properties":{ #定义索引中包含的属性设置
"id":{ #设置索引中包含id属性
"type":"keyword" #当前属性可以被直接搜索
},
"name":{ #设置索引中包含name属性
"type":"text", #当前属性是⽂本信息,参与分词
"analyzer":"ik_max_word", #使⽤IK分词器进⾏分词
"copy_to":"all" #分词结果拷⻉到all属性中
},
"type":{
"type":"keyword"
},
"description":{
"type":"text",
"analyzer":"ik_max_word",
"copy_to":"all"
},
"all":{ #定义属性,⽤来描述多个字段的分词结果集合,当前属性可以参与查询
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
操作⽂档
⽬前我们已经有了索引了,但是索引中还没有数据,所以要先添加数据,ES中称数据为⽂档,下⾯进⾏⽂档操作。
添加⽂档,有三种⽅式
POST请求 http://localhost:9200/products/_doc #使⽤系统⽣成id
POST请求 http://localhost:9200/products/_create/1 #使⽤指定id
POST请求 http://localhost:9200/products/_doc/1 #使⽤指定id,不存在创建,存在更新(版本递增)⽂档通过请求参数传递,数据格式json
查询⽂档
GET请求 http://localhost:9200/products/_doc/1 #查询单个⽂档
GET请求 http://localhost:9200/products/_search #查询全部⽂档
条件查询
GET请求 http://localhost:9200/products/_search?q=name:springboot# q=查询属性名:查询属性值
删除⽂档
DELETE请求 http://localhost:9200/products/_doc/1
修改⽂档(全量更新)
PUT请求 http://localhost:9200/products/_doc/1
⽂档通过请求参数传递,数据格式json
修改⽂档(部分更新)
POST请求 http://localhost:9200/products/_update/1
⽂档通过请求参数传递,数据格式json
{
"doc":{ #部分更新并不是对原始⽂档进⾏更新,⽽是对原始⽂档
对象中的doc属性中的指定属性更新
"name":"springboot" #仅更新提供的属性值,未提供的属性值不参
与更新操作
}
}
4 springBoot整合ES
springBoot整合ES的模式有两种,第⼀种模式springBoot收录了ES,但是随着ES版本迭代,新版的ES在springBoot中是没有收录的,需要我们⼿动的整合。
springBoot与⽼版本ES整合步骤
1.导⼊springboot整合ES的starter坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
2.进⾏基础配置 yml
配置ES服务器地址,端⼝9200
spring: elasticsearch: uris: localhost:9200
3.使⽤
springboot整合ES的专⽤客户端接⼝ElasticsearchRestTemplate来进⾏操作
@SpringBootTest
class Demo10SpringbootEsApplicationTests {
@Autowired
private ElasticsearchRestTemplate template;
@Test
void contextLoads() {
System.out.println(template);
}
}
上述操作形式是ES早期的操作⽅式,使⽤的客户端被称为Low Level Client,这种客户端操作⽅式性能⽅⾯略显不⾜,于是ES开发了全新的客户端操作⽅式,称为High Level Client。⾼级别客户端与ES版本同步更新,但是springboot最初整合ES的时候使⽤的是低级别客户端,所以企业开发需要更换成⾼级别的客户端模式。
springBoot与新版本ES整合
操作步骤如下:
1.导⼊springboot整合ES⾼级别客户端的坐标
<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.9</version> </dependency>
2.使⽤编程的形式设置连接的ES服务器,并获取客户端对象
@SpringBootTest
class EsApplicationTests {
//声明客户端
private RestHighLevelClient client;
@Test
void contextLoads() {
//初始化客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
//创建索引
CreateIndexRequest request = new CreateIndexRequest("products");
try {
client.indices().create(request, RequestOptions.DEFAULT);
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
⾼级别客户端操作是通过发送请求的⽅式完成所有操作的,ES针对各种不同的操作,设定了各式各样的请求对象,上例中创建索引的对象是CreateIndexRequest,其他操作也会有⾃⼰专⽤的Request对象。
当前操作我们发现,⽆论进⾏ES何种操作,第⼀步永远是获取RestHighLevelClient对象,最后⼀步永远是关闭该对象的连接。在测试中可以使⽤测试类的特性去帮助开发者⼀次性的完成上述操作,但是在业务书写时,还需要⾃⾏管理。将上述代码格式转换成使⽤测试类的初始化⽅法和销毁⽅法进⾏客户端对象的维护。
@SpringBootTest
class EsApplicationTests {
//声明客户端
private RestHighLevelClient client;
@BeforeEach//在测试类中每个操作运⾏前运⾏的⽅法
void setUp() {
//初始化客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
}
@AfterEach//在测试类中每个操作运⾏后运⾏的⽅法
void tearDown() {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
void contextLoads() {
//创建索引
CreateIndexRequest request = new CreateIndexRequest("products");
try {
client.indices().create(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
}
}
创建索引(IK分词器)
@SpringBootTest
class EsApplicationTests {
//声明客户端
private RestHighLevelClient client;
@BeforeEach//在测试类中每个操作运⾏前运⾏的⽅法
void setUp() {
//初始化客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
}
@AfterEach//在测试类中每个操作运⾏后运⾏的⽅法
void tearDown() {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 使用IK分词器创建索引
*/
@Test
void testIK(){
//创建索引
CreateIndexRequest request = new CreateIndexRequest("products");
String jsonStr = "{\n" +
" \"mappings\":{\n" +
" \"properties\":{\n" +
" \"id\":{\n" +
" \"type\":\"keyword\"\n" +
" },\n" +
" \"name\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\",\n" +
" \"copy_to\":\"all\"\n" +
" },\n" +
" \"type\":{\n" +
" \"type\":\"keyword\"\n" +
" },\n" +
" \"description\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\",\n" +
" \"copy_to\":\"all\"\n" +
" },\n" +
" \"all\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
//给请求对象设置参数
request.source(jsonStr, XContentType.JSON);
try {
client.indices().create(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
}
}
IK分词器是通过请求参数的形式进⾏设置的,设置请求参数使⽤request对象中的source⽅法进⾏设置,⾄于参数是什么,取决于你的操作种类。当请求中需要参数时,均可使⽤当前形式进⾏参数设置。
添加⽂档:
@SpringBootTest
class EsApplicationTests {
//声明客户端
private RestHighLevelClient client;
@BeforeEach//在测试类中每个操作运⾏前运⾏的⽅法
void setUp() {
//初始化客户端
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
client = new RestHighLevelClient(builder);
}
@AfterEach//在测试类中每个操作运⾏后运⾏的⽅法
void tearDown() {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建一个文档
*/
@Test
void testDoc() throws IOException {
IndexRequest request = new IndexRequest("products").id("5");
String json = "{\n" +
" \"name\":\"goods\",\n" +
" \"type\":\"goods\",\n" +
" \"description\":\"goods\"\n" +
"}";
request.source(json, XContentType.JSON);
client.index(request, RequestOptions.DEFAULT);
}
}
添加⽂档使⽤的请求对象是IndexRequest,与创建索引使⽤的请求对象不同。
批量添加⽂档:
创建一个实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Products {
private String id;
private String type;
private String name;
private String description;
}
在测试类
/**
* 批量添加文档
*/
@Test
void testBulk() throws IOException {
//模拟数据
List<Products> list = new ArrayList<>();
list.add(new Products("6","aaa","bbbb","cccc"));
list.add(new Products("7","ddd","eeee","ffff"));
list.add(new Products("8","ggg","hhhh","iiii"));
//创建一个批量请求对象
BulkRequest bulk = new BulkRequest();
for(Products products:list){
IndexRequest request = new IndexRequest("products").id(products.getId());
String json = JSON.toJSONString(products);
request.source(json,XContentType.JSON);
bulk.add(request);
}
client.bulk(bulk,RequestOptions.DEFAULT);
}
批量做时,先创建⼀个BulkRequest的对象,可以将该对象理解为是⼀个保存request对象的容器,将所有的请求都初始化好后,添加到BulkRequest对象中,再使⽤BulkRequest对象的bulk⽅法,⼀次性执⾏完毕。
按id查询⽂档:
/**
* 根据id查询文档
*/
@Test
void testById() throws IOException {
GetRequest request = new GetRequest("products","5");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
String json = response.getSourceAsString();
System.out.println(json);
}
根据id查询⽂档使⽤的请求对象是GetRequest。
按条件查询⽂档:
/**
* 根据条件查询文档
*/
@Test
void testSearch() throws IOException {
//获取条件对象
SearchRequest request = new SearchRequest("products");
//设置文档查询条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.termQuery("name","goods"));
request.source(builder);
//查询操作
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
for(SearchHit hit : hits){
String source = hit.getSourceAsString();
System.out.println(source);
}
}
按条件查询⽂档使⽤的请求对象是SearchRequest,查询时调⽤SearchRequest对象的termQuery⽅法,需要给出查询属性名,此处⽀持使⽤合并字段,也就是前⾯定义索引属性时添加的all属性。
与前期进⾏springboot整合redis和mongodb的差别还是蛮⼤的,主要原始就是我们没有使⽤springboot整合ES的客户端对象。⾄于操作,由于ES操作种类过多,所以显得操作略微有点复杂。
总结
springboot整合ES步骤
1. 导⼊springboot整合ES的High Level Client坐标
2. ⼿⼯管理客户端对象,包括初始化和关闭操作
3. 使⽤High Level Client根据操作的种类不同,选择不同的Request对象完成
对应操作