使用SpringBoot操作ElasticSearch
我正在参加「掘金·启航计划」
使用SpringBoot连接操作ES的方法有很多
官方表示
TransportClient在 ES7中已经不推荐使用,在ES8中将删除
强烈建议您使用High Level REST Client而不是
TransportClient
。
所以本次的示例使用 spring-boot-starter-data-elasticsearch
进行操作
官方的版本建议 建议根据官方来
Spring Data Elasticsearch | Elasticsearch | Spring Boot |
---|---|---|
4.0.x | 7.6.2 | 2.3.x |
3.2.x | 6.8.6 | 2.2.x |
3.1.x | 6.2.2 | 2.1.x |
3.0.x | 5.5.0 | 2.0.x |
2.1.x | 2.4.0 | 1.5.x |
一、准备阶段
1. 创建SpringBoot项目 名称为 elastic-search-study-spring-boot
2. 添加 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
3. 创建实体类 User
用于数据的传输
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import java.io.Serializable;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "yufire", type = "user")
public class User implements Serializable {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String name;
@Field
private Integer age;
@Field
private Date birthday;
@Field
private String desc;
private static final long serialVersionUID = -2193182831746543212L;
}
- @Document 文档
- indexName 索引名称
- type 类型名称
- @Id 表明这个字段在 es中是id
- @Field 表示这个字段是es中的普通属性
- 也可以指定这个字段的类型 和 分词类型
- 不指定类型也可以 会根据 Java 类型来自动指定
二、继承接口
Spring Data存储库抽象中的中央接口是Repository
, 它需要类型以及类的ID类型作为类型参数来进行管理
我们只需要继承 ElasticsearchRepository<T,ID>接口即可
1. 创建一个接口 userDao
在 继承接口后边指定 类型,和类型ID的类型
package cn.yufire.esstudy3.dao;
import cn.yufire.esstudy3.pojo.User;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ESDao extends ElasticsearchRepository<User, String> {
}
2. 创建索引 创建类型 添加数据
如果es中没有该索引 会根据实体类中配置的索引信息来自动创建索引和类型
@Resource
private ESDao esDao;
@Test
void contextLoads() {
User user = new User();
user.setId("1");
user.setName("张三");
user.setAge(22);
user.setBirthday(new Date());
user.setDesc("第一位用户");
User save = esDao.save(user);
System.out.println(save);
}
控制台
User(id=1, name=张三, age=22, birthday=Sat Jul 25 11:55:18 CST 2020, desc=第一位用户)
可以发现es中多了个 索引并且数据也别添加了进去
3. 批量添加数据
对象可以从数据库中查询出来 在添加到 es 中去
只需要调用 saveAll 方法即可
// 创建四个对象
User user1 = new User("2", "小花", 20, new Date(), "第二名测试用户");
User user2 = new User("3", "小绿", 21, new Date(), "第三名测试用户");
User user3 = new User("4", "小红", 22, new Date(), "第四名测试用户");
User user4 = new User("5", "小大", 28, new Date(), "第五名测试用户");
// 把数据添加到 list中去
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
// 添加到 es中去
Iterable<User> addUsers = esDao.saveAll(users);
System.out.println(addUsers);
控制台
User(id=2, name=小花, age=20, birthday=Sat Jul 25 12:00:27 CST 2020, desc=第二名测试用户)
User(id=3, name=小绿, age=21, birthday=Sat Jul 25 12:00:27 CST 2020, desc=第三名测试用户) User(id=4, name=小红, age=22, birthday=Sat Jul 25 12:00:27 CST 2020, desc=第四名测试用户)
User(id=5, name=小大, age=28, birthday=Sat Jul 25 12:00:27 CST 2020, desc=第五名测试用户)
4. 修改数据
在 ES 中 修改数据就是保存一个新的数据然后覆盖以前的数据 只要 Id 一致 就可以完成修改
User user1 = new User("2", "小花花", 20, new Date(), "第二名测试用户");
esDao.save(user1);
5. 删除数据
User user1 = new User("2", "小花花", 20, new Date(), "第二名测试用户");
// 根据id 删除
esDao.deleteById("2");
// 根据对象删除
esDao.delete(user1);
// 根据集合删除
esDao.deleteAll(你的集合);
// 删除 该索引中所有的数据
esDao.deleteAll();
6. match查询
Iterable<User> search = esDao.search(QueryBuilders.matchQuery("name", "小"));
7. term查询
Iterable<User> search = esDao.search(QueryBuilders.termQuery("name", "小"));
8. range查询
Iterable<User> search = esDao.search(QueryBuilders.rangeQuery("age").gt(20).lt(30));
9. 多条件查询
// 创建 bool子句对象
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("name", "小"));
boolQueryBuilder.mustNot(QueryBuilders.termQuery("age", 18));
boolQueryBuilder.should(QueryBuilders.matchQuery("name", "小"));
boolQueryBuilder.should(QueryBuilders.termQuery("age", 18));
boolQueryBuilder.filter(QueryBuilders.rangeQuery("age").gt(20).lt(30));
esDao.search(boolQueryBuilder);
10. 分页查询
只需要在 查询对象后边多加上一个参数(分页请求) 并指定页码即可
esDao.search(boolQueryBuilder, PageRequest.of(0, 10));
11. 排序查询
只需要在后边添加排序参数 以及排序的字段即可
Iterable<User> search = esDao.search(boolQueryBuilder, PageRequest.of(0, 2, Sort.by(Sort.Order.asc("age"))));
三、高级REST客户端
官方强烈推荐使用的 操作ES方式
1. 创建一个 RestClient
- RestClient.builder 内可以配置多个主机 已完成集群操作
- 因为是Rest客户端所以时使用HTTP方式进行操作 所以要使用 9200端口
/**
* <p>
* Description: ES 高级客户端配置类
* </p>
*
* @author yufire
* @version v1.0.0
* @see cn.yufire.esstudy3.config
* @since 2020-07-25 21:10:42
*/
@Configuration
public class ESRestClientConfig {
/**
* 因为是Rest客户端所以时使用HTTP方式进行操作
* 所以要使用 9200端口
*
* @return
*/
@Bean
public RestHighLevelClient elasticsearchClient() {
return new RestHighLevelClient(
RestClient.builder(
new HttpHost("elastic", 9200, "http")
//new HttpHost("elastic", 9200, "http")
));
}
}
一、索引操作
1. 创建索引
- 如果 只是想单纯的创建索引 那么可以把 设置 类型 和 其他配置的那两行代码删掉即可!
// 创建一个创建索引的请求 并且指定索引名称
CreateIndexRequest createIndexRequest = new CreateIndexRequest("yufire");
String json = "{\n" +
" \"properties\": {\n" +
" \"name\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\":\"ik_smart\"\n" +
" },\n" +
" \"age\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"birthday\":{\n" +
" \"type\": \"date\"\n" +
" },\n" +
" \"desc\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }";
// 设置类型 属性JSON字符串 设置数据的格式
createIndexRequest.mapping("user", json, XContentType.JSON);
// 设置索引的一些配置 分片 和 副本 数量
createIndexRequest.settings(Settings.builder()
.put("index.number_of_shards", 3)
.put("index.number_of_replicas", 2));
// 发送创建索引的请求
CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest);
// 判断是否创建成功
System.out.println(createIndexResponse.isAcknowledged());
2. 删除索引
// 创建删除索引的请求 并且指定索引名
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("yufire");
// 一次删除多个索引的操作
deleteIndexRequest.indices("yufire", "yufire1", "yufire2", "....");
// 发送请求
DeleteIndexResponse delete = client.indices().delete(deleteIndexRequest);
// 判断是否删除成功
System.out.println(delete.isAcknowledged());
二、文档操作
1. 添加文档
// 创建 添加文档的请求 并指定索引名称和类型名称
IndexRequest indexRequest = new IndexRequest("yufire", "user");
// 创建一个对象
User user = new User("1", "小王", 22, new Date(), "第一名用户");
// 添加到请求中去 要转换为JSON格式 ,数据的格式
indexRequest.source(JSON.toJSONString(user), XContentType.JSON);
// 发送请求
IndexResponse index = client.index(indexRequest);
// 获取状态码 201 为创建成功
System.out.println(index.status().getStatus());
2. 根据Id获取文档
// 创建请求 字段索引 类型 id
GetRequest getRequest = new GetRequest("yufire", "user", "kUlYhnMBP9XWT10qGJVe");
// 发送请求
GetResponse documentFields = client.get(getRequest);
// 获取到结果 在转换为 User类型
User user = JSONObject.parseObject(documentFields.getSourceAsString(), User.class);
System.out.println(user);
3. 修改文档
- 在 ES 中 修改文档其实就是覆盖文档 以达到修改的效果
// 创建请求 字段索引 类型 id
UpdateRequest updateRequest = new UpdateRequest("yufire", "user", "lElqhnMBP9XWT10qAJVt");
// 放入要修改的文档
User user = new User("1", "小王吧", 22, new Date(), "第一名用户");
updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
// 发送请求
UpdateResponse delete = client.update(updateRequest);
// 获取响应状态码 200 为修改成功
System.out.println(delete.status().getStatus());
4. 删除文档
// 创建请求 字段索引 类型 id
DeleteRequest deleteRequest = new DeleteRequest("yufire", "user", "kUlYhnMBP9XWT10qGJVe");
// 发送请求
DeleteResponse delete = client.delete(deleteRequest);
// 获取响应状态码 200 为删除成功
System.out.println(delete.status().getStatus());
三、批量操作
1. 批量新增
// 创建批量操作请求
BulkRequest bulkRequest = new BulkRequest();
// 创建四个对象
User user1 = new User("2", "小花", 20, new Date(), "第二名测试用户");
User user2 = new User("3", "小绿", 21, new Date(), "第三名测试用户");
User user3 = new User("4", "小红", 22, new Date(), "第四名测试用户");
User user4 = new User("5", "小大", 28, new Date(), "第五名测试用户");
// 把数据添加到 list中去 模拟走数据库中查询的结果
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
// 把数据添加值请求中
for (User user : users) {
bulkRequest.add(new IndexRequest("yufire", "user")
.source(JSON.toJSONString(user), XContentType.JSON)
);
}
// 发送请求
BulkResponse bulk = client.bulk(bulkRequest);
// 查询是否失败 false 为没有失败 就是成功
System.out.println(bulk.hasFailures());
2. 批量删除
3. 批量修改
只需要批量添加不同的请求类型即可
四、搜索
1. 多id搜索
// 创建搜索请求对象
SearchRequest searchRequest = new SearchRequest("yufire");
//创建搜索参数构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 根据多个id进行查询 , 指定索引类型
IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery("user");
// 添加id
idsQueryBuilder.addIds("l0mlhnMBP9XWT10qw5Vl", "mEmlhnMBP9XWT10qw5Vl", "lElqhnMBP9XWT10qAJVt");
// 条件放入条件构造器
searchSourceBuilder.query(idsQueryBuilder);
// 把条件构造器放入 请求对象中
searchRequest.source(searchSourceBuilder);
// 发送搜索请求
SearchResponse search = client.search(searchRequest);
// 解析结果
for (SearchHit hit : search.getHits().getHits()) {
// 把结果转换为 User类型
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println(user);
}
2. mathc查询
// 创建 match搜索对象
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "小");
// 条件放入条件构造器
searchSourceBuilder.query(matchQueryBuilder);
3. term搜索
// 创建 term搜索
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "小");
// 条件放入条件构造器
searchSourceBuilder.query(termQueryBuilder);
4. range搜索
// 创建 range 搜索条件
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gt(18).lt(20);
// 条件放入条件构造器
searchSourceBuilder.query(rangeQueryBuilder);
5. 多条件搜索
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.should(QueryBuilders.matchQuery("name", "小"));
boolQueryBuilder.should(QueryBuilders.termQuery("name", "小"));
boolQueryBuilder.must(QueryBuilders.rangeQuery("age").gt(18).lt(20));
boolQueryBuilder.filter(QueryBuilders.matchQuery("name", "小绿"));
// 把条件构造器放入 请求对象中
searchRequest.source(searchSourceBuilder);
6. 排序
// 添加排序选项
searchSourceBuilder.sort(new FieldSortBuilder("age").order(SortOrder.ASC));
7. 分页
// 设置起始页码
searchSourceBuilder.from(0);
// 设置页面容量
searchSourceBuilder.size(2);
五、高亮查询(重点)
- 高亮一般用于查询关键字的突出显示
- 用来提醒用户 提升用户体验 是我们的程序更加的美好!
以下是代码
// 创建搜索请求对象
SearchRequest searchRequest = new SearchRequest("yufire");
//创建搜索参数构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 创建 match搜索对象
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "小");
// 条件放入条件构造器
searchSourceBuilder.query(matchQueryBuilder);
// 创建高亮条件构造器
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 设置高亮字段
highlightBuilder.field("name");
// 设置 前缀标签 结果会出现在 两个标签的中间
highlightBuilder.preTags("<span color='red'>");
// 设置 后缀标签 结果会出现在 两个标签的中间
highlightBuilder.postTags("</span>");
// 放入搜索构造中去
searchSourceBuilder.highlighter(highlightBuilder);
// 把条件构造器放入 请求对象中
searchRequest.source(searchSourceBuilder);
// 发送搜索请求
SearchResponse search = client.search(searchRequest);
// 解析结果
for (SearchHit hit : search.getHits().getHits()) {
// 定义替换结果后的Map
Map<String, Object> sourceAsMap = null;
// 获取到高亮字段
HighlightField name = hit.getHighlightFields().get("name");
// 循环查询结果中符合条件的数据条数
for (Text fragment : name.getFragments()) {
// 获取到高亮字段 替换后的结果
String highField = fragment.toString();
// 获取到原先的数据集合
sourceAsMap = hit.getSourceAsMap();
// 循环替换
sourceAsMap.put("name", highField);
}
// 输出替换后的结果
System.out.println(sourceAsMap);
}
控制台
- 可以看到 name属性的数据已经变成我们替换为高亮结果了
{birthday=1595691876949, name=<span color='red'>小</span>大, id=5, age=28, desc=第五名测试用户}
{birthday=1595691876949, name=<span color='red'>小</span>绿, id=3, age=21, desc=第三名测试用户}