Spring Boot整合elasticsearch
目前常用的Spring Boot整合elasticsearch的java客户端有:
-
spring-boot-starter-data-elasticsearch
官方文档:https://docs.spring.io/spring-data/elasticsearch/docs/4.2.1/reference/html/#preface
-
Java High Level REST Client
spring-boot-starter-data-elasticsearch、spring-data-elasticsearch,由Spring提供的elasticsearch的封装,上手简单(类似JPA的操作),缺点是更新慢,SpringBoot 2.2.x才提供对es7.x的支持。
Java High Level REST Client由官方推出,其代码语法和DSL语法(前文讲的ES操作语法)很相似,前文学了高级操作语法后用此java客户端就是熟悉api和用法的问题,和DSL语法一样用法较灵活。
下面是这两种客户端与Spring Boot的简单整合(es版本为7.2)。
一、spring-boot-starter-data-elasticsearch
为支持es7.x版本,整合用的spring boot版本升级到了2.2.0
1 依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
2 yml配置文件
spring: elasticsearch: rest: uris: http://192.168.75.128:9200 username: elastic password: 123456
3 编辑实体类(对象映射)
参考官网:https://docs.spring.io/spring-data/elasticsearch/docs/4.2.1/reference/html/#elasticsearch.mapping
几种重要的摘出来:
-
@Document: 应用于类级别,表示该类是映射到数据库的候选。最重要的属性是:
- indexName:存储该实体的索引名称。这可以包含一个 SpEL 模板表达式,如 “log-#{T(java.time.LocalDate).now().toString()}”
-
@Id
:在字段级别应用以标记用于标识目的的字段。 -
@Field
:应用于字段级别,定义字段的属性,大部分属性映射到各自的Elasticsearch Mapping定义(以下列表不完整,完整参考Javadoc注解):name
: 字段名称,因为它将在 Elasticsearch 文档中表示,如果未设置,则使用 Java 字段名称。type
:字段类型,可以是Text、Keyword、Long、Integer、Short、Byte、Double、Float、Half_Float、Scaled_Float、Date、Date_Nanos、Boolean、Binary、Integer_Range、Float_Range、Long_Range、Double_Range、Date_Range、Ip_Range、Object之一, 嵌套, Ip, TokenCount, Percolator, Flattened, Search_As_You_Type。
例子:
@Document(indexName = "shop", type = "_doc") public class Shop { @Id private String id; @Field(type = FieldType.Keyword) private String name; @Field(type = FieldType.Keyword) private String price; }
4 编辑持久层操作类
类似于JPA的操作,根据实体类创建Repository接口进行操作。
例子:
public interface ShopRepository extends ElasticsearchRepository<Shop, String> { }
Repository封装好的方法有:
也可以自定义方法以及使用ElasticsearchRestTemplate来操作。
5 根据方法名称自动实现自定义方法
不过需要遵从一定的约定:方法命名和参数
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 | findByAvailableTrueOrderByNameDesc | {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}} |
6 高级查询
若想实现复杂的查询或统计,就要通过QueryBuilders实现高级查询,详查API,基本用法如下:
// 基本查询 TermQueryBuilder termQuery = QueryBuilders.termQuery(name, value); shopRepository.search(termQuery);// 执行查询 // 另外还有其它基本查询 QueryBuilders.matchQuery(name, text); QueryBuilders.rangeQuery("").lt(to); // ... NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 多条件查询 queryBuilder.withQuery(QueryBuilders.termQuery("name", "ww")); queryBuilder.withQuery(QueryBuilders.rangeQuery("").gt("")); // 设置分页参数 queryBuilder.withPageable(PageRequest.of(0, 10)); // 排序 queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC)); // 执行查询 shopRepository.search(queryBuilder.build()); // 聚合,类型为terms,聚合名称为names,聚合字段为name queryBuilder.addAggregation( AggregationBuilders.terms("names").field("name")); // 执行查询,需要把结果强转为AggregatedPage类型 AggregatedPage<Shop> aggPage = (AggregatedPage<Shop>) this.shopRepository.search(queryBuilder.build()); // 组合查询 BoolQueryBuilder baseQueryBuild = QueryBuilders.boolQuery(); // 查询 price=12 and (name = ls or name = zs) BoolQueryBuilder shouldQueryBuild = QueryBuilders.boolQuery(); shouldQueryBuild.should(QueryBuilders.termQuery("name", "ls")); shouldQueryBuild.should(QueryBuilders.termQuery("name", "zs")); baseQueryBuild.filter(QueryBuilders.termQuery("price", "12")); baseQueryBuild.filter(shouldQueryBuild); // 执行查询 this.shopRepository.search(baseQueryBuild);
二、Java High Level REST Client
1 依赖
<dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.2.0</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> <version>7.2.0</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.2.0</version> </dependency>
2 yml配置文件
elasticsearch: uris: http://192.168.75.128:9200 username: elastic password: 123456
3 客户端连接配置类
import java.util.ArrayList; import java.util.List; import javax.net.ssl.SSLContext; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.impl.nio.reactor.IOReactorConfig; import org.apache.http.ssl.SSLContextBuilder; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; import lombok.SneakyThrows; @Configuration public class ElasticSearchConfig { @Value("${elasticsearch.uris}") private String uris; @Value("${elasticsearch.username}") private String username; @Value("${elasticsearch.password}") private String password; @Bean(name = "restHighLevelClient") public RestHighLevelClient restHighLevelClient() { // 拆分地址 List<HttpHost> hostLists = new ArrayList<>(); String[] hostList = uris.split(","); for (String addr : hostList) { hostLists.add(HttpHost.create(addr)); } // 转换成 HttpHost 数组 HttpHost[] httpHost = hostLists.toArray(new HttpHost[] {}); RestClientBuilder clientBuilder = RestClient.builder(httpHost); clientBuilder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { @SneakyThrows @Override public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); credentialsProvider.setCredentials(AuthScope.ANY, credentials); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); } IOReactorConfig ioReactorConfig = IOReactorConfig.custom().setIoThreadCount(100) .setConnectTimeout(10000).setSoTimeout(10000).build(); httpClientBuilder.setDefaultIOReactorConfig(ioReactorConfig); SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(java.security.cert.X509Certificate[] arg0, String arg1) { return true; } }).build(); httpClientBuilder.setSSLContext(sslContext); httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); return httpClientBuilder; } }); return new RestHighLevelClient(clientBuilder); } }
使用时注入ElasticSearchConfig:
@Autowired RestHighLevelClient esClient;
4 基本使用
包含:
- 文档操作(IndexRequest)
- 查询操作(GetRequest)
- 删除操作(DeleteRequest)
- 更新操作(UpdateRequest)
- 批量操作(BulkRequest)
- 。。。
5 高级查询
最基本的查询写法:
SearchRequest searchRequest = new SearchRequest(); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchAllQuery()); searchRequest.source(searchSourceBuilder);
QueryBuilders
和spring-boot-starter-data-elasticsearch的QueryBuilders同源,用法一样
多看API,熟能生巧
附:发现了一套对High Level REST Client的成熟封装代码
上传到了gitee上:https://gitee.com/zhangchaohuiget/csdn-learn/tree/master/ElasticSearchJavaClient/HignLevelClientDemo