一、ElasticsearchRestTemplate
前面学习了es rest接口对es进行操作的方式,并且还学习了es的分片及扩容,有讲解了几种常见的分词器,喜欢的小伙伴可以看下本专栏的其他文章,本篇主要将 在 SpringBoot 中使用ElasticsearchRestTemplate
对ES进行操作。
对于SpringBoot对ES的操作早在以前我就写过一篇文章,但那时基于ES 6.X 的,在新版本的ES中都已经不建议使用了。
注意: 在网上还有些教程是使用TransportClient
进行操作的,在 Elasticsearch 7.0 中不建议使用TransportClient,并且在8.0中会完全删除TransportClient。因此,官方更建议我们用Java High Level REST Client
,它执行HTTP请求,而不是序列化的Java请求。
我们的使用的 ElasticsearchRestTemplate
就是基于RestHighLevelClient
的再一层封装,下面我们一起开始吧。
二、SpringBoot 整合 ES 客户端环境
首先新建一个SpringBoot项目,再pom
中引入es的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
在application.yml配制文件中,配制es的连接:
server:
port: 8080
spring:
elasticsearch:
rest:
uris: http://127.0.0.1:9200 #多个地址用逗号分隔
# username: #用户名
# password: #密码
connection-timeout: 6000
read-timeout: 6000
到此环境就已经搭建好了,下面开始对es进行操作吧。
三、ElasticsearchRestTemplate 操作ES
首先新建一个ES的entity
类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "user", shards = 3, replicas = 1, refreshInterval = "30s")
public class UserEsEntity {
@Id
private String id;
@Field(type = FieldType.Text, searchAnalyzer = "ik_max_word", analyzer = "ik_smart")
private String name;
@Field(type = FieldType.Keyword)
private String sex;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Keyword, index = false)
private String grade;
}
定义索引名称为user,分片为3,每个分片的副本是1,刷新时间是30s。
下面可以新建一个ElasticsearchRepository
,其实可以直接用ElasticsearchRestTemplate
就可以进行es的操作了,而且Repository
中很多方法都被标了过时,这里大家也可以看下:
@Repository
public interface UserEsDao extends ElasticsearchRepository<UserEsEntity, Long> {
}
对于查询已经不建议使用ElasticsearchRepository
了,下面直接学习ElasticsearchRestTemplate
的Api吧。
1. 判断索引是否存在
其实在前面写好Entity,启动项目如果索引不存在就会自动创建的,这里也可以通过Api的方式进行验证:
@Slf4j
@RestController
@RequestMapping("/rest")
public class RestTemplateController {
@Autowired
ElasticsearchRestTemplate elasticsearchRestTemplate;
@GetMapping("/indexExists")
public String indexExists() {
boolean r1 = elasticsearchRestTemplate.indexExists(UserEsEntity.class);
boolean r2 = elasticsearchRestTemplate.indexExists("user2");
// 创建索引
// elasticsearchRestTemplate.createIndex()
log.info("r1: {} , r2: {}", r1, r2);
return "success";
}
}
2. 新增文档数据
@GetMapping("/save")
public String save() {
UserEsEntity esEntity = new UserEsEntity(null, "张三", "男", 18, "1");
// 不要拿到自增的返回值
// UserEsEntity save = elasticsearchRestTemplate.save(esEntity);
// log.info(save.toString());
// 拿到自增的返回值
IndexQuery indexQuery = new IndexQueryBuilder()
// .withId(esEntity.getId())
.withObject(esEntity)
.build();
String id = elasticsearchRestTemplate.index(indexQuery, IndexCoordinates.of("user"));
log.info("添加的id:{} ", id);
// 批量添加
// List<UserEsEntity> list = new ArrayList<>();
// list.add(esEntity);
// list.add(esEntity);
// elasticsearchRestTemplate.save(list);
return "success";
}
3. 更新文档数据
其中根据条件批量修改需要使用RestHighLevelClient
,直接注入即可:
@Autowired
RestHighLevelClient restHighLevelClient;
@GetMapping("/update")
public String update() throws IOException {
// 覆盖修改
// UserEsEntity esEntity = new UserEsEntity("W9MVD34BVYNyxUnr8cdE", "lisi", "男", 20, "1");
// elasticsearchRestTemplate.save(esEntity);
// UpdateQuery query = UpdateQuery.builder("W9MVD34BVYNyxUnr8cdE");
// 根据ID 修改某个字段
// Document document = Document.create();
// document.putIfAbsent("name", "wangwu2"); //更新后的内容
// UpdateQuery updateQuery = UpdateQuery.builder("W9MVD34BVYNyxUnr8cdE")
// .withDocument(document)
// .withRetryOnConflict(5) //冲突重试
// .withDocAsUpsert(true) //不加默认false。true表示更新时不存在就插入
// .build();
// UpdateResponse response = elasticsearchRestTemplate.update(updateQuery, IndexCoordinates.of("user"));
// log.info(response.getResult().toString()); //UPDATED 表示更新成功
// 根据条件批量更新 ,使用 RestHighLevelClient
// https://blog.csdn.net/weixin_34318272/article/details/88690004
// 参数为索引名,可以不指定,可以一个,可以多个
UpdateByQueryRequest request = new UpdateByQueryRequest("user");
// 版本冲突
request.setConflicts("proceed");
// 设置查询条件
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
// 并且 and
queryBuilder.must(QueryBuilders.termQuery("name", "lisi"));
queryBuilder.must(QueryBuilders.termQuery("sex", "男"));
request.setQuery(queryBuilder);
// 批次大小
request.setBatchSize(1000);
// 并行
request.setSlices(2);
// 使用滚动参数来控制“搜索上下文”存活的时间
request.setScroll(TimeValue.timeValueMinutes(10));
// 刷新索引
request.setRefresh(true);
// 更新的内容
request.setScript(new Script("ctx._source['name']='wangwu'"));
BulkByScrollResponse response = restHighLevelClient.updateByQuery(request, RequestOptions.DEFAULT);
log.info(response.getStatus().getUpdated() + ""); // 返回1 表示成功
return "success";
}
4. 删除文档数据
@GetMapping("/delete")
public String delete() {
// 根据id删除
// String r = elasticsearchRestTemplate.delete("XNMVD34BVYNyxUnr8cdE", IndexCoordinates.of("user"));
// log.info("r : {} ", r);
// 根据条件删除
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
// 并且 and
queryBuilder.must(QueryBuilders.termQuery("name", "zhangsan"));
queryBuilder.must(QueryBuilders.termQuery("sex", "男"));
Query query = new NativeSearchQuery(queryBuilder);
elasticsearchRestTemplate.delete(query, UserEsEntity.class, IndexCoordinates.of("user"));
return "success";
}
5. 查询文档数据
@GetMapping("/search")
public String search() {
// 查询全部数据
// QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
// 精确查询 =
// QueryBuilder queryBuilder = QueryBuilders.termQuery("name", "lisi");
// 精确查询 多个 in
// QueryBuilder queryBuilder = QueryBuilders.termsQuery("name", "张三", "lisi");
// match匹配,会把查询条件进行分词,然后进行查询,多个词条之间是 or 的关系,可以指定分词
// QueryBuilder queryBuilder = QueryBuilders.matchQuery("name", "张三");
// QueryBuilder queryBuilder = QueryBuilders.matchQuery("name", "张三").analyzer("ik_max_word");
// match匹配 查询多个字段
// QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery("男", "name", "sex");
// fuzzy 模糊查询,返回包含与搜索字词相似的字词的文档。
// QueryBuilder queryBuilder = QueryBuilders.fuzzyQuery("name","lisx");
// prefix 前缀检索
// QueryBuilder queryBuilder = QueryBuilders.prefixQuery("name","张");
// wildcard 通配符检索
// QueryBuilder queryBuilder = QueryBuilders.wildcardQuery("name","张*");
// regexp 正则查询
QueryBuilder queryBuilder = QueryBuilders.regexpQuery("name", "(张三)|(lisi)");
// boost 评分权重,令满足某个条件的文档的得分更高,从而使得其排名更靠前。
queryBuilder.boost(2);
// 多条件构建
// BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
// 并且 and
// queryBuilder.must(QueryBuilders.termQuery("name", "张三"));
// queryBuilder.must(QueryBuilders.termQuery("sex", "女"));
// 或者 or
// queryBuilder.should(QueryBuilders.termQuery("name", "张三"));
// queryBuilder.should(QueryBuilders.termQuery("name", "lisi"));
// 不等于,去除
// queryBuilder.mustNot(QueryBuilders.termQuery("name", "lisi"));
// 过滤数据
// queryBuilder.filter(QueryBuilders.matchQuery("name", "张三"));
// 范围查询
/*
gt 大于 >
gte 大于等于 >=
lt 小于 <
lte 小于等于 <=
*/
// queryBuilder.filter(new RangeQueryBuilder("age").gt(10).lte(50));
// 构建分页,page 从0开始
Pageable pageable = PageRequest.of(0, 3);
Query query = new NativeSearchQueryBuilder()
.withQuery(queryBuilder)
.withPageable(pageable)
//排序
.withSort(SortBuilders.fieldSort("_score").order(SortOrder.DESC))
//投影
.withFields("name")
.build();
SearchHits<UserEsEntity> search = elasticsearchRestTemplate.search(query, UserEsEntity.class);
log.info("total: {}", search.getTotalHits());
Stream<SearchHit<UserEsEntity>> searchHitStream = search.get();
List<UserEsEntity> list = searchHitStream.map(SearchHit::getContent).collect(Collectors.toList());
log.info("结果数量:{}", list.size());
list.forEach(entity -> {
log.info(entity.toString());
});
return "success";
}
喜欢的小伙伴可以关注我的个人微信公众号,获取更多学习资料!