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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!