本文是我在学习es的笔记,如有疏漏,敬请指出
一、创建并启动es的docker容器
#部署es,版本为7.12.1,端口号为9200
#部署es
docker run -d \
--name es \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
-v es-data:/usr/share/elasticsearch/data \
-v es-plugins:/usr/share/elasticsearch/plugins \
--privileged \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:7.12.1
#部署kibana,版本为7.12.1,端口号为5601
#部署kibana
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601 \
kibana:7.12.1
!两者版本必须相同
打开浏览器,进入kibana控制台
红色为虚拟机的ip,黄色为设置的kibana端口号。
打开开发工具,就可以在kibana控制台内进行创建索引库、插入文档等操作
二、在Idea内利用RestClient操作索引库
docker容器必须成功部署
——添加restclient依赖
<!--elasticSearch依赖-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
在使用springboot时需要进行版本控制,在properties内控制版本,也正因如此上面的依赖中材不需要版本信息。
<elasticsearch.version>7.12.1</elasticsearch.version>
这样版本才不会出现错误。
——创建client对象,并设置在启动前创建并连接es(连接地址改成自己的地址)
//创建client对象
private RestHighLevelClient client;
//启动设置
@BeforeEach
void setUp(){
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.168.124:9200")
));
}
//关闭设置
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
验证初始化是否成功
@Test
void testInit() {
System.out.println("初始化成功");
}
——利用RestClient创建、删除、查询(是否存在)、查询(得到的是地址)索引库
三步走:
1.创建相应的xxIndexRequest对象
2.设置(创建、删除、查询)参数
3.用client发送请求
创建时,要输入dsl语句,这里创建了一个静态变量
//创建
@Test
void createHotelIndex() throws IOException {
//创建request对象
CreateIndexRequest request = new CreateIndexRequest("hotel");
//准备请求参数,dsl语句
request.source(HotelConstants.MAPPING_TEMPLATE, XContentType.JSON);
//发送创建请求
client.indices().create(request, RequestOptions.DEFAULT);
}
//删除
@Test
void deleteIndex() throws IOException {
//创建request对象
DeleteIndexRequest request = new DeleteIndexRequest();
//删除的名称
request.indices("hotel");
//发送删除请求
client.indices().delete(request, RequestOptions.DEFAULT);
}
//查询是否存在
@Test
void existIndex() throws IOException {
//创建request对象
GetIndexRequest request = new GetIndexRequest("hotel");
//查询的名称
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
//打印查询信息
System.err.println(exists ? "索引库存在" : "索引库不存在");
}
//查询索引库
@Test
void getIndex() throws IOException {
//创建request对象
GetIndexRequest request = new GetIndexRequest("hotel");
//发送查询请求并打印
System.out.println(client.indices().get(request, RequestOptions.DEFAULT));
}
三、利用RestClient操作文档(CRUD)
——C(create),新增文档
首先还是对client进行初始化
//创建client对象
private RestHighLevelClient client;
@Test
void testInit() {
System.out.println("初始化成功");
}
//启动设置
@BeforeEach
void setUp(){
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.168.124:9200")
));
}
//关闭设置
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
初始化成功之后,可以进行新增操作
以下是新增文档的方法(细节在下面有补充)
//新增文档(create-C)
@Test
void createDocument() throws IOException {
//在数据库中查询数据
HotelDoc hotelDoc = new HotelDoc(hotelService.getById(39106L));
//创建request对象(id只能是小写,有大写会出现错误)
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
//输入配置
request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);
//发送请求
client.index(request,RequestOptions.DEFAULT);
}
1.利用mybatisPlus在数据库中根据id进行查询
首先要引入mybatisPlus的依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
然后在application.yml文件中让mybatisPlus识别对象,并且完成数据库名到驼峰命名的转换
这里在查询完会自动导出到Hotel的POJO类
但是创建的文档中的元素跟Hotel类的元素有些许差别,因此又创建了HotelDoc的POJO类与创建文档所需要的元素类型相同,如果本来就相同可以不创建。
HotelDoc内有接受Hotel类参数并且创建对象的构造方法
接着在类名上加上@SpringBootTest注解,并自动注入IHotelService接口
到此从数据库中根据id查询并且自动装配到pojo的流程就结束了,对应第一步
2.创建request并且输入要查询的索引库名,文档名。
文档名字要小写
3.利用FastJson配置mapping
3.1引入FastJson依赖
<!--FastJson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.71</version>
</dependency>
3.2用FastJson提供的API把对象转换为Json格式
4.发送请求
这里直接使用client的index方法,因为是创建文档,而不像之前的创建索引库
5.查询新增文档是否成功
在kibana开发工具中查询是否添加成功
查询结果
可以看到已经成功实现了新增文档的操作--查询到了!
——R(retrieve),查询文档
有了之前的依赖注入和配置,接下来只是类似的操作了,只不过使用的方法有些许不同
不过多赘述
不同:
1.R(retrieve)使用getRequest
2.需要解析结果,用了getSourceAsString方法
//查询文档(retrieve-R)
@Test
void retrieveDocument() throws IOException {
//创建request对象
GetRequest request = new GetRequest("hotel").id("39106");
//发送请求
GetResponse response = client.get(request, RequestOptions.DEFAULT);
//解析并查看结果
String sourceAsString = response.getSourceAsString();
System.out.println(sourceAsString);
}
——U(update),修改文档
不同:
1.U(update)使用updateRequest
2.需要准备更新的数据,使用request.doc,注意格式
——D(delete),删除文档
不同:
1.D(delete)使用deleteRequest
四、批量操作文档
不同:
1.使用bulkRequest
2.遍历了数据库得到所有数据,然后循环遍历修改格式并添加到bulkRequest中,CRUD都可以添加,添加方法就是用request.add,添加不同的操作在add方法的参数中输入不同的CRUD操作的XXrequest即可
五、使用RestClient查询文档
——MatchAll查询
match all就是查询索引库中的所有文档
在kibana中上面两种操作的结果是一样的
在idea中利用RestClient操作如下图
大体分为两步
1.创建并发送请求
与之前在操作文档时类似,不过在这里要创建的request是searchRequest(“索引名”)。在查询时主要利用官方提供的两个API(source和QueryBuilders)
用source选择需要实现的查询功能
用QueryBuilders实现DQL语句的选择和编辑
需要什么从API中找就可以了
2.解析结果
可以看到在查询得到的结果中有包含总条数total、和查找结果数组hits【】等信息的hits(与前面的数组做区分),在解析时只需要一层一层地拿到
下面的查询的基本步骤都与MatchAll相同,对解析结果的代码进行了封装(getResult)下面只指出主要的不同点
——Match查询
match则是根据输入的query条件进行查询
对应的RestClient操作如下图
不同:
1.QueryBuilders的使用不同,使用了matchQuery的API并指定了要查询的词条和内容
——term、range、boolean查询
term查询是全局精确查询,查询结果必须跟输入的信息一致
range查询是范围查询
boolean查询是条件判断查询,有must、mustNot、filter、should等条件,其中还有一些与评分机制有关,感兴趣的可以自行查找
不同:
1.QueryBuilders的使用不同,创建了一个BoolQueryBuilder并编辑了要查询的词条和内容
2.使用链式编程在编辑查询条件时加入了term和range查询,意思是:城市必须在上海,价格在300以下
——分页和排序
分页使用的是from(第几页)和size(每页的大小),排序使用sort指定要排序的词条和升降序
不同:
1.这里不再重点关注查询,而是对source的操作,这里使用了MatchAll
2.使用链式编程在编辑source时加入了sort和from、size,意思是:对价格进行升序排序,从当前页(3)开始,每页有7条数据
——高亮设置
不同:
1.创建了HighlightBuilder这里两种创建方法是一样的
new HighlightBuilder()和SearchSourceBuilder.highlight()
2.对source的操作(使用的是highlighter),首先进行一次全文匹配Match查询,然后配置highlighter,最后使用链式编程进行高亮设置,意思是:“city”字段高亮,且不需要与查询字段匹配
3.结果解析过程不同
解析过程添加了以下获取、判断和覆盖的过程
如下图,可以看到在hit【】数组的元素hit中source和highlight是平级的
根据之前的解析过程,在循环中可以直接用 hit.getHighlightFields() 获取高亮相关的信息,得到的是一个Map集合,然后获取集合中的高亮字段并对原字段进行覆盖
运行后的结果可以看到确实进行了覆盖
——代码
package cn.itcast.hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.util.Map;
public class HotelSearchTest {
//创建client对象
private RestHighLevelClient client;
@Test
void testInit() {
System.out.println("初始化成功");
}
//启动设置
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.168.124:9200")
));
}
//关闭设置
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
//MatchAll
@Test
void testMatchAll() throws IOException {
//创建请求
SearchRequest request = new SearchRequest("hotel");
//编辑请求中的dsl语句
request.source().query(QueryBuilders.matchAllQuery());
//发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析并打印结果
getResult(response);
}
//Match
@Test
void testMatch() throws IOException {
//创建请求
SearchRequest request = new SearchRequest("hotel");
//编辑请求中的dsl语句
request.source().query(QueryBuilders.matchQuery("city", "上海"));
//发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析并打印结果
getResult(response);
}
//term、range、boolean查询
@Test
void testTRB() throws IOException {
//创建请求
SearchRequest request = new SearchRequest("hotel");
//编辑请求中的dsl语句
//创建一个BoolQueryBuilder
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//编辑BoolQueryBuilder
boolQuery.must(QueryBuilders.termQuery("city","上海"))
.filter(QueryBuilders.rangeQuery("price").lte(300));
//传输给source
request.source().query(boolQuery);
//发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析并打印结果
getResult(response);
}
//PageAndSort
@Test
void testPageAndSort() throws IOException {
//当前页数和每页的大小
int page = 3, size = 7;
//创建请求
SearchRequest request = new SearchRequest("hotel");
//编辑请求中的dsl语句
request.source().query(QueryBuilders.matchAllQuery())
.sort("price", SortOrder.ASC)
.from((page - 1) * size)
.size(size);
//发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析并打印结果
getResult(response);
}
//HighLight
@Test
void testHigh() throws IOException {
//创建请求
SearchRequest request = new SearchRequest("hotel");
//编辑请求中的dsl语句
request.source().query(QueryBuilders.matchQuery("city", "上海"))
.highlighter(SearchSourceBuilder.highlight()
.field("city")
.requireFieldMatch(false));
//发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//解析并打印结果
getResult(response);
}
//解析
private void getResult(SearchResponse response) {
//得到hits数组
SearchHit[] hits = response.getHits().getHits();
//遍历得到每条hit
for (SearchHit hit : hits) {
//得到每条hit的source
String sourceFromHit = hit.getSourceAsString();
//利用Fastjson反序列化,输入要反序列化的对象的class字节码
HotelDoc parsed2HotelDoc = JSON.parseObject(sourceFromHit, HotelDoc.class);
//获取高亮结果的集合
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//判断集合是否为空,不为空则执行高亮覆盖
if (!CollectionUtils.isEmpty(highlightFields)) {
//根据搜索内容(city对应 键)获取高亮结果(highlightField对应 值)
HighlightField highlightField = highlightFields.get("city");
//判断高亮结果是否空
if(highlightField != null){
//得到高亮的字段
String highlightSection = highlightField.getFragments()[0].string();
//覆盖非高亮结果
parsed2HotelDoc.setCity(highlightSection);
}
}
//打印
System.out.println(parsed2HotelDoc);
}
}
}