springboot中使用ElasticSearch的详细教程
新建项目
新建一个springboot项目springboot_es
用于本次与ElasticSearch的整合,如下图
引入依赖
修改我们的pom.xml
,加入spring-boot-starter-data-elasticsearch
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
编写配置文件
由于ElasticSearch从7.x版本开始淡化TransportClient甚至于在8.x版本中遗弃,所以spring data elasticsearch推荐我们使用rest客户端RestHingLevelClient
(端口号使用9200)以及接口ElasticSearchRespositoy
。
- RestHighLevelClient 更强大,更灵活,但是不能友好的操作对象
- ElasticSearchRepository 对象操作友好
首先我们编写配置文件如下
/** * ElasticSearch Rest Client config * @author Christy * @date 2021/4/29 19:40 **/ @Configuration public class ElasticSearchRestClientConfig extends AbstractElasticsearchConfiguration{ @Override @Bean public RestHighLevelClient elasticsearchClient() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("192.168.8.101:9200") .build(); return RestClients.create(clientConfiguration).rest(); } }
springboot操作ES
RestHighLevelClient方式
有了上面的rest client,我们就可以在其他的地方注入该客户端对ElasticSearch进行操作。我们新建一个测试文件,使用客户端对ElasticSearch进行基本的操作
1.注入RestClient
/** * ElasticSearch Rest client操作 * * RestHighLevelClient 更强大,更灵活,但是不能友好的操作对象 * ElasticSearchRepository 对象操作友好 * * 我们使用rest client 主要测试文档的操作 * @Author Christy * @Date 2021/4/29 19:51 **/ @SpringBootTest public class TestRestClient { // 复杂查询使用:比如高亮查询 @Autowired private RestHighLevelClient restHighLevelClient; }
2.插入一条文档
/** * 新增一条文档 * @author Christy * @date 2021/4/29 20:17 */ @Test public void testAdd() throws IOException { /** * 向ES中的索引christy下的type类型中添加一天文档 */ IndexRequest indexRequest = new IndexRequest("christy","user","11"); indexRequest.source("{\"name\":\"齐天大圣孙悟空\",\"age\":685,\"bir\":\"1685-01-01\",\"introduce\":\"花果山水帘洞美猴王齐天大圣孙悟空是也!\"," + "\"address\":\"花果山\"}", XContentType.JSON); IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT); System.out.println(indexResponse.status()); }
我们可以看到文档插入成功,我们去kibana中查询该条文档
完全没有问题的。
3.删除一条文档
/** * 删除一条文档 * @author Christy * @date 2021/4/29 20:18 */ @Test public void deleteDoc() throws IOException { // 我们把特朗普删除了 DeleteRequest deleteRequest = new DeleteRequest("christy","user","rYBNG3kBRz-Sn-2f3ViU"); DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT); System.out.println(deleteResponse.status()); } }
4.更新一条文档
/** * 更新一条文档 * @author Christy * @date 2021/4/29 20:19 */ @Test public void updateDoc() throws IOException { UpdateRequest updateRequest = new UpdateRequest("christy","user","p4AtG3kBRz-Sn-2fMFjj"); updateRequest.doc("{\"name\":\"调皮捣蛋的hardy\"}",XContentType.JSON); UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT); System.out.println(updateResponse.status()); }
5.批量更新文档
/** * 批量更新 * @author Christy * @date 2021/4/29 20:42 */ @Test public void bulkUpdate() throws IOException { BulkRequest bulkRequest = new BulkRequest(); // 添加 IndexRequest indexRequest = new IndexRequest("christy","user","13"); indexRequest.source("{\"name\":\"天蓬元帅猪八戒\",\"age\":985,\"bir\":\"1685-01-01\",\"introduce\":\"天蓬元帅猪八戒因调戏嫦娥被贬下凡\",\"address\":\"高老庄\"}", XContentType.JSON); bulkRequest.add(indexRequest); // 删除 DeleteRequest deleteRequest01 = new DeleteRequest("christy","user","pYAtG3kBRz-Sn-2fMFjj"); DeleteRequest deleteRequest02 = new DeleteRequest("christy","user","uhTyGHkBExaVQsl4F9Lj"); DeleteRequest deleteRequest03 = new DeleteRequest("christy","user","C8zCGHkB5KgTrUTeLyE_"); bulkRequest.add(deleteRequest01); bulkRequest.add(deleteRequest02); bulkRequest.add(deleteRequest03); // 修改 UpdateRequest updateRequest = new UpdateRequest("christy","user","10"); updateRequest.doc("{\"name\":\"炼石补天的女娲\"}",XContentType.JSON); bulkRequest.add(updateRequest); BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT); BulkItemResponse[] items = bulkResponse.getItems(); for (BulkItemResponse item : items) { System.out.println(item.status()); } }
在kibana中查询结果
6.查询文档
@Test public void testSearch() throws IOException { //创建搜索对象 SearchRequest searchRequest = new SearchRequest("christy"); //搜索构建对象 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchAllQuery())//执行查询条件 .from(0)//起始条数 .size(10)//每页展示记录 .postFilter(QueryBuilders.matchAllQuery()) //过滤条件 .sort("age", SortOrder.DESC);//排序 //创建搜索请求 searchRequest.types("user").source(searchSourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); System.out.println("符合条件的文档总数: "+searchResponse.getHits().getTotalHits()); System.out.println("符合条件的文档最大得分: "+searchResponse.getHits().getMaxScore()); SearchHit[] hits = searchResponse.getHits().getHits(); for (SearchHit hit : hits) { System.out.println(hit.getSourceAsMap()); } }
ElasticSearchRepository方式
1.准备工作
ElasticSearchRepository方式主要通过注解和对接口实现的方式来实现ES的操作,我们在实体类上通过注解配置ES索引的映射关系后,当实现了ElasticSearchRepository接口的类第一次操作ES进行插入文档的时候,ES会自动生成所需要的一切。但是该种方式无法实现高亮查询,想要实现高亮查询只能使用RestHighLevelClient
开始之前我们需要熟悉一下接口方式为我们提供的注解,以及编写一些基础的类
1.清空ES数据
2.了解注解
@Document
: 代表一个文档记录
indexName
: 用来指定索引名称
type
: 用来指定索引类型
@Id
: 用来将对象中id和ES中_id映射
@Field
: 用来指定ES中的字段对应Mapping
type
: 用来指定ES中存储类型
analyzer
: 用来指定使用哪种分词器
3.新建实体类
/** * 用在类上作用:将Emp的对象映射成ES中一条json格式文档 * indexName: 用来指定这个对象的转为json文档存入那个索引中 要求:ES服务器中之前不能存在此索引名 * type : 用来指定在当前这个索引下创建的类型名称 * * @Author Christy * @Date 2021/4/29 21:22 */ @Data @Document(indexName = "christy",type = "user") public class User { @Id //用来将对象中id属性与文档中_id 一一对应 private String id; // 用在属性上 代表mapping中一个属性 一个字段 type:属性 用来指定字段类型 analyzer:指定分词器 @Field(type = FieldType.Text,analyzer = "ik_max_word") private String name; @Field(type = FieldType.Integer) private Integer age; @Field(type = FieldType.Date) @JsonFormat(pattern = "yyyy-MM-dd") private Date bir; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String content; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String address; }
4.UserRepository
/** * @Author Christy * @Date 2021/4/29 21:23 **/ public interface extends ElasticsearchRepository<User,String> { }
5.TestUserRepository
/** * @Author Christy * @Date 2021/4/29 21:51 **/ @SpringBootTest public class TestUserRepository { @Autowired private UserRepository userRepository; }
2.保存文档
@Test public void testSaveAndUpdate(){ User user = new User(); // id初识为空,此操作为新增 user.setId(UUID.randomUUID().toString()); user.setName("唐三藏"); user.setBir(new Date()); user.setIntroduce("西方世界如来佛祖大弟子金蝉子转世,十世修行的好人,得道高僧!"); user.setAddress("大唐白马寺"); userRepository.save(user); }
3.修改文档
@Test public void testSaveAndUpdate(){ User user = new User(); // 根据id修改信息 user.setId("1666eb47-0bbf-468b-ab45-07758c741461"); user.setName("唐三藏"); user.setBir(new Date()); user.setIntroduce("俗家姓陈,状元之后。西方世界如来佛祖大弟子金蝉子转世,十世修行的好人,得道高僧!"); user.setAddress("大唐白马寺"); userRepository.save(user); }
4.删除文档
repository接口默认提供了4种删除方式,我们演示根据id进行删除
@Test public void deleteDoc(){ userRepository.deleteById("1666eb47-0bbf-468b-ab45-07758c741461"); }
5.检索一条记录
@Test public void testFindOne(){ Optional<User> optional = userRepository.findById("1666eb47-0bbf-468b-ab45-07758c741461"); System.out.println(optional.get()); }
6.查询所有
@Test public void testFindAll(){ Iterable<User> all = userRepository.findAll(); all.forEach(user-> System.out.println(user)); }
7.排序
@Test public void testFindAllSort(){ Iterable<User> all = userRepository.findAll(Sort.by(Sort.Order.asc("age"))); all.forEach(user-> System.out.println(user)); }
8.分页
@Test public void testFindPage(){ //PageRequest.of 参数1: 当前页-1 Page<User> search = userRepository.search(QueryBuilders.matchAllQuery(), PageRequest.of(1, 1)); search.forEach(user-> System.out.println(user)); }
9.自定义查询
先给大家看一个表,是不是很晕_(¦3」∠)_
Keyword | Sample | Elasticsearch Query String |
---|---|---|
And |
findByNameAndPrice |
{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Or |
findByNameOrPrice |
{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Is |
findByName |
{"bool" : {"must" : {"field" : {"name" : "?"}}}} |
Not |
findByNameNot |
{"bool" : {"must_not" : {"field" : {"name" : "?"}}}} |
Between |
findByPriceBetween |
{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
LessThanEqual |
findByPriceLessThan |
{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
GreaterThanEqual |
findByPriceGreaterThan |
{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Before |
findByPriceBefore |
{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
After |
findByPriceAfter |
{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Like |
findByNameLike |
{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
StartingWith |
findByNameStartingWith |
{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
EndingWith |
findByNameEndingWith |
{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} |
Contains/Containing |
findByNameContaining |
{"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}} |
In |
findByNameIn (Collection<String>names) |
{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
NotIn |
findByNameNotIn (Collection<String>names) |
{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
Near |
findByStoreNear |
Not Supported Yet ! |
True |
findByAvailableTrue |
{"bool" : {"must" : {"field" : {"available" : true}}}} |
False |
findByAvailableFalse |
{"bool" : {"must" : {"field" : {"available" : false}}}} |
OrderBy |
findByAvailable TrueOrderByNameDesc |
{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}} |
这个表格看起来复杂,实际上也不简单,但是确实很牛逼。我们只要按照上面的定义在接口中定义相应的方法,无须写实现就可实现我们想要的功能
举个例子,上面有个findByName是下面这样定义的
假如我们现在有个需求需要按照名字查询用户,我们可以在UserRepository
中定义一个方法,如下
// 根据姓名查询 List<User> findByName(String name);
系统提供的查询方法中findBy
是一个固定写法,像上面我们定义的方法findByName
,其中Name是我们实体类中的属性名,这个必须对应上。也就是说这个findByName不仅仅局限于name
,还可以findByAddress、findByAge等等;
现在就拿findByName来讲,我们要查询名字叫唐三藏的用户
@Test public void testFindByName(){ List<User> userList = userRepository.findByName("唐三藏"); userList.forEach(user-> System.out.println(user)); }
其实就是框架底层直接使用下面的命令帮我们实现的查询
GET /christy/user/_search { "query": { "bool": { "must": [ { "term": { "name":"?" } } ] } } }
10.高亮查询
我们上面说了,ElasticSearchRepository实现不了高亮查询,想要实现高亮查询还是需要使用RestHighLevelClient方式。最后我们使用rest clientl实现一次高亮查询
@Test public void testHighLightQuery() throws IOException, ParseException { // 创建搜索请求 SearchRequest searchRequest = new SearchRequest("christy"); // 创建搜索对象 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.termQuery("introduce", "唐僧")) // 设置查询条件 .from(0) // 起始条数(当前页-1)*size的值 .size(10) // 每页展示条数 .sort("age", SortOrder.DESC) // 排序 .highlighter(new HighlightBuilder().field("*").requireFieldMatch(false).preTags("<span style='color:red;'>").postTags("</span>")); // 设置高亮 searchRequest.types("user").source(searchSourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] hits = searchResponse.getHits().getHits(); List<User> userList = new ArrayList<>(); for (SearchHit hit : hits) { Map<String, Object> sourceAsMap = hit.getSourceAsMap(); User user = new User(); user.setId(hit.getId()); user.setAge(Integer.parseInt(sourceAsMap.get("age").toString())); user.setBir(new SimpleDateFormat("yyyy-MM-dd").parse(sourceAsMap.get("bir").toString())); user.setIntroduce(sourceAsMap.get("introduce").toString()); user.setName(sourceAsMap.get("name").toString()); user.setAddress(sourceAsMap.get("address").toString()); Map<String, HighlightField> highlightFields = hit.getHighlightFields(); if(highlightFields.containsKey("name")){ user.setName(highlightFields.get("name").fragments()[0].toString()); } if(highlightFields.containsKey("introduce")){ user.setIntroduce(highlightFields.get("introduce").fragments()[0].toString()); } if(highlightFields.containsKey("address")){ user.setAddress(highlightFields.get("address").fragments()[0].toString()); } userList.add(user); } userList.forEach(user -> System.out.println(user)); }
到此这篇关于ElasticSearch在springboot中使用的详细教程的文章就介绍到这了,更多相关springboot使用ElasticSearch内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
相关文章
解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题
这篇文章主要介绍了解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-04-28- 这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
- 这篇文章主要介绍了详解springBoot启动时找不到或无法加载主类解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
Elasticsearch工具cerebro的安装与使用教程
这篇文章主要介绍了Elasticsearch工具cerebro的安装与使用教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-08- 这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
- 这篇文章主要介绍了解决Springboot get请求是参数过长的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-17
Spring Boot项目@RestController使用重定向redirect方式
这篇文章主要介绍了Spring Boot项目@RestController使用重定向redirect方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-02- 这篇文章主要介绍了Springboot+TCP监听服务器搭建过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-10-28
- 这篇文章主要介绍了springBoot 项目排除数据库启动方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-10
docker 启动elasticsearch镜像,挂载目录后报错的解决
这篇文章主要介绍了docker 启动 elasticsearch镜像,挂载目录后报错的解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-20详解SpringBoot之访问静态资源(webapp...)
这篇文章主要介绍了详解SpringBoot之访问静态资源(webapp...),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-14- 这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
springboot中使用@Transactional注解事物不生效的坑
这篇文章主要介绍了springboot中使用@Transactional注解事物不生效的原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-26- 这篇文章主要介绍了springboot多模块包扫描问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-16
- 这篇文章主要介绍了Springboot实现多线程注入bean的工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-27
Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用详解
这篇文章主要介绍了Springboot mybatis plus druid多数据源解决方案 dynamic-datasource的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-18Springboot+MDC+traceId日志中打印唯一traceId
本文主要介绍了Springboot+MDC+traceId日志中打印唯一traceId,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-17SpringBoot部署到Linux读取resources下的文件及遇到的坑
本文主要给大家介绍SpringBoot部署到Linux读取resources下的文件,在平时业务开发过程中,很多朋友在获取到文件内容乱码或者文件读取不到的问题,今天给大家分享小编遇到的坑及处理方案,感兴趣的朋友跟随小编一起看看吧...2021-06-21- 这篇文章主要介绍了springboot中nacos动态路由的配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-11
SpringBoot高版本修改为低版本时测试类报错的解决方案
这篇文章主要介绍了SpringBoot高版本修改为低版本时测试类报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-18