本文主要是对 elasticsearch-rest-high-level-client 是学习总结。
1、es端口:
默认情况下,ElasticSearch使用两个端口来监听外部TCP流量。
- 9200端口:用于所有通过HTTP协议进行的API调用。包括搜索、聚合、监控、以及其他任何使用HTTP协议的请求。所有的客户端库都会使用该端口与ElasticSearch进行交互。
- 9300端口:是一个自定义的二进制协议,用于集群中各节点之间的通信。用于诸如集群变更、主节点选举、节点加入/离开、分片分配等事项。
以往,9300端口也被用于客户端库的连接,然而这种类型的交互在我们的官方客户端已被废弃,其他地方也不支持。
2、es的java客户端
客户端 | 优点 | 缺点 | 说明 |
Java Low Level Rest Client | 与ES版本之间没有关系,适用于作为所有版本ES的客户端 | ||
Java High Level Rest Client | 使用最多 | 使用需与ES版本保持一致 | 基于Low Level Rest Client,它提供了更多的接口。注意:7.15版本之后将被弃用 |
TransportClient | 使用Transport 接口进行通信,能够使用ES集群中的一些特性,性能最好 | JAR包版本需与ES集群版本一致,ES集群升级,客户端也跟着升级到相同版本 | 过时产品,7版本之后不再支持 |
Elasticsearch Java API Client | 最新的es客户端 | 文档少 |
详细的elasticsearch java客户端发展史详见:/cloudbigdata/article/details/126296206
3、RestHighLevelClient介绍
JavaREST客户端有两种模式:
- Java Low Level REST Client:ES官方的低级客户端。低级别的客户端通过http与Elasticearch集群通信。
- Java High Level REST Client:ES官方的高级客户端。基于上面的低级客户端,也是通过HTTP与ES集群进行通信。它提供了更多的接口。
注意事项:
客户端(Client) Jar包的版本尽量不要大于Elasticsearch本体的版本,否则可能出现客户端中使用的某些API在Elasticsearch中不支持。
4、springboot集成RestHighLevelClient
下面介绍下 SpringBoot 如何通过 elasticsearch-rest-high-level-client 工具操作ElasticSearch。当然也可以通过spring-data-elasticsearch来操作ElasticSearch,而本文仅是 elasticsearch-rest-high-level-client 的案例介绍。
这里需要说一下,能使用RestHighLevelClient尽量使用它,为什么不推荐使用 Spring 家族封装的 spring-data-elasticsearch。主要原因是灵活性和更新速度,Spring 将 ElasticSearch 过度封装,让开发者很难跟 ES 的 DSL 查询语句进行关联。再者就是更新速度,ES 的更新速度是非常快,但是 spring-data-elasticsearch 更新速度比较缓慢。并且spring-data-elasticsearch在和版本上的Java API差距很大,如果升级版本需要花点时间来了解。spring-data-elasticsearch的底层其实也是基于elasticsearch-rest-high-level-client的api。
4.1、maven依赖
<!--引入es-high-level-client相关依赖 start-->
<dependency>
<groupId></groupId>
<artifactId>elasticsearch</artifactId>
<version>6.8.2</version>
</dependency>
<dependency>
<groupId></groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>6.8.2</version>
</dependency>
<dependency>
<groupId></groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.8.2</version>
</dependency>
<!--引入es-high-level-client相关依赖 end-->
<!--加入json解析 start-->
<dependency>
<groupId></groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!--加入json解析 end-->
4.2、es配置
4.2.1、 配置文件
# es集群名称
=single-node-cluster
#es用户名
=elastic
#es密码
=elastic
# es host ip 地址(集群):本次使用的是单机模式
=43.142.243.124:9200
# es 请求方式
=http
# es 连接超时时间
=1000
# es socket 连接超时时间
=30000
# es 请求超时时间
=500
# es 最大连接数
=100
# es 每个路由的最大连接数
=100
4.2.2、java 连接配置类
写一个 Java 配置类读取 application 中的配置信息:
package ;
import .slf4j.Slf4j;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import org.;
import org.;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
/**
* restHighLevelClient 客户端配置类
*/
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticsearchConfig {
// es host ip 地址(集群)
private String hosts;
// es用户名
private String userName;
// es密码
private String password;
// es 请求方式
private String scheme;
// es集群名称
private String clusterName;
// es 连接超时时间
private int connectTimeOut;
// es socket 连接超时时间
private int socketTimeOut;
// es 请求超时时间
private int connectionRequestTimeOut;
// es 最大连接数
private int maxConnectNum;
// es 每个路由的最大连接数
private int maxConnectNumPerRoute;
/**
* 如果@Bean没有指定bean的名称,那么这个bean的名称就是方法名
*/
@Bean(name = "restHighLevelClient")
public RestHighLevelClient restHighLevelClient() {
// 拆分地址
// List<HttpHost> hostLists = new ArrayList<>();
// String[] hostList = (",");
// for (String addr : hostList) {
// String host = (":")[0];
// String port = (":")[1];
// (new HttpHost(host, (port), scheme));
// }
// // 转换成 HttpHost 数组
// HttpHost[] httpHost = (new HttpHost[]{});
// 此处为单节点es
String host = (":")[0];
String port = (":")[1];
HttpHost httpHost = new HttpHost(host,(port));
// 构建连接对象
RestClientBuilder builder = (httpHost);
// 设置用户名、密码
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
(,new UsernamePasswordCredentials(userName,password));
// 连接延时配置
(requestConfigBuilder -> {
(connectTimeOut);
(socketTimeOut);
(connectionRequestTimeOut);
return requestConfigBuilder;
});
// 连接数配置
(httpClientBuilder -> {
(maxConnectNum);
(maxConnectNumPerRoute);
(credentialsProvider);
return httpClientBuilder;
});
return new RestHighLevelClient(builder);
}
}
4.3、mybatis配置
package ;
import ;
import ;
public interface GoodsMapper {
/**
* 查询所有
*/
List<Goods> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN" "/dtd/">
<mapper namespace="">
<select resultType="">
select `id`,
`title`,
`price`,
`stock`,
`saleNum`,
`createTime`,
`categoryName`,
`brandName`,
`status`,
`spec`
from goods
</select>
</mapper>
4.4、实体对象
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
public class Goods {
/**
* 商品编号
*/
private Long id;
/**
* 商品标题
*/
private String title;
/**
* 商品价格
*/
private BigDecimal price;
/**
* 商品库存
*/
private Integer stock;
/**
* 商品销售数量
*/
private Integer saleNum;
/**
* 商品分类
*/
private String categoryName;
/**
* 商品品牌
*/
private String brandName;
/**
* 上下架状态
*/
private Integer status;
/**
* 说明书
*/
private String spec;
/**
* 商品创建时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
public Goods() {
}
public Goods(Long id, String title, BigDecimal price, Integer stock, Integer saleNum, String categoryName, String brandName, Integer status, String spec, Date createTime) {
= id;
= title;
= price;
= stock;
= saleNum;
= categoryName;
= brandName;
= status;
= spec;
= createTime;
}
public Long getId() {
return id;
}
public void setId(Long id) {
= id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
= title;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
= price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
= stock;
}
public Integer getSaleNum() {
return saleNum;
}
public void setSaleNum(Integer saleNum) {
= saleNum;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
= categoryName;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
= brandName;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
= status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
= createTime;
}
public String getSpec() {
return spec;
}
public void setSpec(String spec) {
= spec;
}
@Override
public String toString() {
return "Goods{" +
", title='" + title + '\'' +
", price=" + price +
", stock=" + stock +
", saleNum=" + saleNum +
", categoryName='" + categoryName + '\'' +
", brandName='" + brandName + '\'' +
", status=" + status +
", spec='" + spec + '\'' +
", createTime=" + createTime +
'}';
}
}
5、索引操作service
IndexTestService:
package ;
import ;
import ;
public interface IndexTestService {
public boolean indexCreate() throws Exception;
public Map<String,Object> getMapping(String indexName) throws Exception;
public boolean indexDelete(String indexName) throws Exception;
public boolean indexExists(String indexName) throws Exception;
}
IndexTestServiceImpl :
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
/**
* 索引服务类
*/
@Service
public class IndexTestServiceImpl implements IndexTestService {
@Autowired
RestHighLevelClient restHighLevelClient;
@Override
public boolean indexCreate() throws Exception {
// 1、创建 创建索引request 参数:索引名mess
CreateIndexRequest indexRequest = new CreateIndexRequest("goods");
// 2、设置索引的settings
// 3、设置索引的mappings
String mapping = "{\n" +
"\n" +
"\t\t\"properties\": {\n" +
"\t\t \"brandName\": {\n" +
"\t\t\t\"type\": \"keyword\"\n" +
"\t\t },\n" +
"\t\t \"categoryName\": {\n" +
"\t\t\t\"type\": \"keyword\"\n" +
"\t\t },\n" +
"\t\t \"createTime\": {\n" +
"\t\t\t\"type\": \"date\",\n" +
"\t\t\t\"format\": \"yyyy-MM-dd HH:mm:ss\"\n" +
"\t\t },\n" +
"\t\t \"id\": {\n" +
"\t\t\t\"type\": \"long\"\n" +
"\t\t },\n" +
"\t\t \"price\": {\n" +
"\t\t\t\"type\": \"double\"\n" +
"\t\t },\n" +
"\t\t \"saleNum\": {\n" +
"\t\t\t\"type\": \"integer\"\n" +
"\t\t },\n" +
"\t\t \"status\": {\n" +
"\t\t\t\"type\": \"integer\"\n" +
"\t\t },\n" +
"\t\t \"stock\": {\n" +
"\t\t\t\"type\": \"integer\"\n" +
"\t\t },\n" +
"\t\t\"spec\": {\n" +
"\t\t\t\"type\": \"text\",\n" +
"\t\t\t\"analyzer\": \"ik_max_word\",\n" +
"\t\t\t\"search_analyzer\": \"ik_smart\"\n" +
"\t\t },\n" +
"\t\t \"title\": {\n" +
"\t\t\t\"type\": \"text\",\n" +
"\t\t\t\"analyzer\": \"ik_max_word\",\n" +
"\t\t\t\"search_analyzer\": \"ik_smart\"\n" +
"\t\t }\n" +
"\t\t}\n" +
" }";
// 4、 设置索引的别名
// 5、 发送请求
// 5.1 同步方式发送请求
IndicesClient indicesClient = ();
(mapping, );
// 请求服务器
CreateIndexResponse response = (indexRequest, );
return ();
}
/**
* 获取表结构
* GET goods/_mapping
*/
@Override
public Map<String, Object> getMapping(String indexName) throws Exception {
IndicesClient indicesClient = ();
// 创建get请求
GetIndexRequest request = new GetIndexRequest(indexName);
// 发送get请求
GetIndexResponse response = (request, );
// 获取表结构
Map<String, MappingMetaData> mappings = ();
Map<String, Object> sourceAsMap = (indexName).getSourceAsMap();
return sourceAsMap;
}
/**
* 删除索引库
*/
@Override
public boolean indexDelete(String indexName) throws Exception {
IndicesClient indicesClient = ();
// 创建delete请求方式
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
// 发送delete请求
AcknowledgedResponse response = (deleteIndexRequest, );
return ();
}
/**
* 判断索引库是否存在
*/
@Override
public boolean indexExists(String indexName) throws Exception {
IndicesClient indicesClient = ();
// 创建get请求
GetIndexRequest request = new GetIndexRequest(indexName);
// 判断索引库是否存在
boolean result = (request, );
return result;
}
}
测试代码:
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import .slf4j.Slf4j;
import ;
import ;
import ;
import ;
import ;
import .;
import ;
import ;
import ;
import ;
@Slf4j
@RunWith()
@SpringBootTest
public class ElasticsearchTest1 {
@Autowired
IndexTestService indexTestService;
/**
* 创建索引库和映射表结构
* 注意:索引一般不会这么创建
*/
@Test
public void indexCreate() {
boolean flag = false;
try {
flag = ();
} catch (Exception e) {
("创建索引失败,错误信息:" + (e));
}
("创建索引是否成功:" + flag);
}
/**
* 获取索引表结构
*/
@Test
public void getMapping() {
try {
Map<String, Object> indexMap = ("goods");
// 将bean 转化为格式化后的json字符串
String pretty1 = (indexMap, , ,
);
("索引信息:{}", pretty1);
} catch (Exception e) {
("获取索引失败,错误信息:" + (e));
}
}
/**
* 删除索引库
*
*/
@Test
public void deleteIndex() {
boolean flag = false;
try {
flag = ("goods");
} catch (Exception e) {
("删除索引库失败,错误信息:" + (e));
}
("删除索引库是否成功:" + flag);
}
/**
* 校验索引库是否存在
*
*/
@Test
public void indexExists() {
boolean flag = false;
try {
flag = ("goods");
} catch (Exception e) {
("校验索引库是否存在,错误信息:" + (e));
}
("索引库是否存在:" + flag);
}
}
6、文档操作service
测试数据:/s/1A_ckKV7wsLJQJoeeALgkig?pwd=r68c
DocumentTestService:
package ;
import ;
import ;
import ;
public interface DocumentTestService {
public RestStatus addDocument(String indexName, String type, Goods goods) throws IOException;
public Goods getDocument(String indexName, String type, String id) throws Exception;
public RestStatus updateDocument(String indexName, String type, Goods goods) throws IOException;
public RestStatus deleteDocument(String indexName, String type, String id) throws IOException;
public RestStatus batchImportGoodsData() throws IOException;
}
DocumentTestServiceImpl :
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import .slf4j.Slf4j;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
/**
* 文档服务类
*/
@Slf4j
@Service
public class DocumentTestServiceImpl implements DocumentTestService {
@Autowired
RestHighLevelClient restHighLevelClient;
@Resource
GoodsMapper goodsMapper;
/**
* 增加文档信息
*/
@Override
public RestStatus addDocument(String indexName, String type, Goods goods) throws IOException {
// 默认类型为_doc
type = (type) ? "_doc" : type;
// 将对象转为json
String data = (goods);
// 创建索引请求对象
IndexRequest indexRequest = new IndexRequest(indexName,type).id(() + "").source(data, );
// 执行增加文档
IndexResponse response = (indexRequest, );
RestStatus status = ();
("创建状态:{}", status);
return status;
}
/**
* 获取文档信息
*/
@Override
public Goods getDocument(String indexName, String type, String id) throws Exception {
// 默认类型为_doc
type = (type) ? "_doc" : type;
// 创建获取请求对象
GetRequest getRequest = new GetRequest(indexName, type, id);
GetResponse response = (getRequest, );
Map<String, Object> sourceAsMap = ();
Goods goods = (sourceAsMap,);
return goods;
}
/**
* 更新文档信息
*/
@Override
public RestStatus updateDocument(String indexName, String type, Goods goods) throws IOException {
// 默认类型为_doc
type = (type) ? "_doc" : type;
// 将对象转为json
String data = (goods);
// 创建索引请求对象
UpdateRequest updateRequest = new UpdateRequest(indexName, type, (()));
// 设置更新文档内容
(data, );
// 执行更新文档
UpdateResponse response = (updateRequest, );
("创建状态:{}", ());
RestStatus status = ();
("更新文档信息响应状态:{}", status);
return status;
}
/**
* 删除文档信息
*/
@Override
public RestStatus deleteDocument(String indexName, String type, String id) throws IOException {
// 默认类型为_doc
type = (type) ? "_doc" : type;
// 创建删除请求对象
DeleteRequest deleteRequest = new DeleteRequest(indexName, type, id);
// 执行删除文档
DeleteResponse response = (deleteRequest, );
RestStatus status = ();
("删除文档响应状态:{}", status);
return status;
}
@Override
public RestStatus batchImportGoodsData() throws IOException {
//1.查询所有数据,mysql
List<Goods> goodsList = ();
//导入
BulkRequest bulkRequest = new BulkRequest();
//2.1 循环goodsList,创建IndexRequest添加数据
for (Goods goods : goodsList) {
//将goods对象转换为json字符串
String data = (goods);//map --> {}
IndexRequest indexRequest = new IndexRequest("goods","_doc");
(() + "").source(data, );
(indexRequest);
}
BulkResponse response = (bulkRequest, );
return ();
}
}
测试代码:
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import .slf4j.Slf4j;
import ;
import ;
import ;
import ;
import ;
import .;
import ;
import ;
import ;
import ;
@Slf4j
@RunWith()
@SpringBootTest
public class ElasticsearchTest1 {
@Autowired
DocumentTestService documentTestService;
/**
* 添加文档
*
*/
@Test
public void addDocument() {
// 创建商品信息
Goods goods = new Goods();
(1L);
("Apple iPhone 13 Pro (A2639) 256GB 远峰蓝色 支持移动联通电信5G 双卡双待手机");
(new BigDecimal("8799.00"));
(1000);
(599);
("手机");
("Apple");
(0);
(new Date());
// 返回状态
RestStatus restStatus = null;
try {
restStatus = ("goods","_doc", goods);
} catch (Exception e) {
("添加文档失败,错误信息:" + (e));
}
("添加文档响应状态:" + restStatus);
}
@Test
public void getDocument() {
// 返回信息
Goods goods = null;
try {
goods = ("goods", "_doc", "1");
} catch (Exception e) {
("查询文档失败,错误信息:" + (e));
}
("查询的文档信息:" + goods);
}
@Test
public void updateDocument() {
// 创建商品信息
Goods goods = new Goods();
("Apple iPhone 13 Pro Max (A2644) 256GB 远峰蓝色 支持移动联通电信5G 双卡双待手机");
(new BigDecimal("9999"));
(1L);
// 返回状态
RestStatus restStatus = null;
try {
restStatus = ("goods", "_doc", goods);
} catch (Exception e) {
("更新文档失败,错误信息:" + (e));
}
("更新文档响应状态:" + restStatus);
}
@Test
public void deleteDocument() {
// 返回状态
RestStatus restStatus = null;
try {
restStatus = ("goods", "_doc", "1");
} catch (Exception e) {
("删除文档失败,错误信息:" + (e));
}
("删除文档响应状态:" + restStatus);
}
/**
* 批量导入测试数据
*/
@Test
public void importDocument() {
// 返回状态
RestStatus restStatus = null;
try {
restStatus = ();
} catch (Exception e) {
("批量导入数据失败,错误信息:" + (e));
}
("批量导入数据响应状态:" + restStatus);
}
}
7、DSL高级查询操作
EsQueryDataService:
package ;
import ;
import ;
import ;
public interface EsQueryDataService {
public <T> List<T> termQuery(String indexName, String columnName, Object value, Class<T> classz);
public <T> List<T> termsQuery(String indexName, String columnName, Object[] dataArgs, Class<T> classz);
public <T> List<T> matchAllQuery(String indexName, Class<T> classz, int startIndex, int pageSize, List<String> orderList, String columnName, Object value);
public <T> List<T> matchPhraseQuery(String indexName, Class<T> classz, String columnName, Object value);
public <T> List<T> matchMultiQuery(String indexName, Class<T> classz, String[] fields, Object text);
public <T> List<T> wildcardQuery(String indexName, Class<T> classz,String field, String text);
public <T> List<T> fuzzyQuery(String indexName, Class<T> classz, String field, String text);
public <T> List<T> boolQuery(String indexName,Class<T> beanClass);
public void metricQuery(String indexName);
public void bucketQuery(String indexName,String bucketField, String bucketFieldAlias);
public void subBucketQuery(String indexName,String bucketField, String bucketFieldAlias,String avgFiled,String avgFiledAlias);
public void subSubAgg(String indexName);
}
EsQueryDataServiceImpl :
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import .slf4j.Slf4j;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import .*;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
@Slf4j
@Service
public class EsQueryDataServiceImpl implements EsQueryDataService {
@Autowired
RestHighLevelClient restHighLevelClient;
/**
* 精确查询(termQuery)
*/
@Override
public <T> List<T> termQuery(String indexName, String field, Object value, Class<T> beanClass) {
// 查询的数据列表
List<T> list = new ArrayList<>();
try {
// 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
((field, value));
// 执行查询es数据
queryEsData(indexName, beanClass, list, searchSourceBuilder);
} catch (IOException e) {
("精确查询数据失败,错误信息:" + (e));
throw new MyBusinessException("99999","精确查询数据失败");
}
return list;
}
/**
* terms:多个查询内容在一个字段中进行查询
*/
@Override
public <T> List<T> termsQuery(String indexName, String field, Object[] dataArgs, Class<T> beanClass) {
// 查询的数据列表
List<T> list = new ArrayList<>();
try {
// 构建查询条件(注意:termsQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
((field, dataArgs));
// 展示100条,默认只展示10条记录
(100);
// 执行查询es数据
queryEsData(indexName, beanClass, list, searchSourceBuilder);
} catch (IOException e) {
("单字段多内容查询数据失败,错误信息:" + (e));
throw new MyBusinessException("99999","单字段多内容查询数据失败");
}
return list;
}
/**
* 匹配查询符合条件的所有数据,并设置分页
*/
@Override
public <T> List<T> matchAllQuery(String indexName, Class<T> beanClass, int startIndex, int pageSize, List<String> orderList, String field, Object value) {
// 查询的数据列表
List<T> list = new ArrayList<>();
try {
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 构建查询条件
if (!(field) && !(value)) {
MatchQueryBuilder matchQueryBuilder = (field, value);
(matchQueryBuilder);
} else {
MatchAllQueryBuilder matchAllQueryBuilder = ();
(matchAllQueryBuilder);
}
// 设置分页
(startIndex);
(pageSize);
// 设置排序
if (orderList != null) {
for(String order : orderList) {
// -开头代表:倒序
boolean flag = ("-");
SortOrder sort = flag ? : ;
order = flag ? (1) : order;
(order, sort);
}
}
// 执行查询es数据
queryEsData(indexName, beanClass, list, searchSourceBuilder);
} catch (IOException e) {
("查询所有数据失败,错误信息:" + (e));
throw new MyBusinessException("99999","查询所有数据失败");
}
return list;
}
/**
* 词语匹配查询
*/
@Override
public <T> List<T> matchPhraseQuery(String indexName, Class<T> beanClass, String field, Object value) {
// 查询的数据列表
List<T> list = new ArrayList<>();
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
((field, value));
// 执行查询es数据
queryEsData(indexName, beanClass, list, searchSourceBuilder);
} catch (IOException e) {
("词语匹配查询失败,错误信息:" + (e));
throw new MyBusinessException("99999","词语匹配查询失败");
}
return list;
}
/**
* 内容在多字段中进行查询
*/
@Override
public <T> List<T> matchMultiQuery(String indexName, Class<T> beanClass, String[] fields, Object text) {
// 查询的数据列表
List<T> list = new ArrayList<>();
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 设置查询条件
((text, fields));
// 执行查询es数据
queryEsData(indexName, beanClass, list, searchSourceBuilder);
} catch (IOException e) {
("词语匹配查询失败,错误信息:" + (e));
throw new MyBusinessException("99999","词语匹配查询失败");
}
return list;
}
/**
* 通配符查询(wildcard):会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)
*
* *:表示多个字符(0个或多个字符)
* ?:表示单个字符
*/
@Override
public <T> List<T> wildcardQuery(String indexName, Class<T> beanClass,String field, String text) {
// 查询的数据列表
List<T> list = new ArrayList<>();
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
((field, text));
// 执行查询es数据
queryEsData(indexName, beanClass, list, searchSourceBuilder);
} catch (IOException e) {
("通配符查询失败,错误信息:" + (e));
throw new MyBusinessException("99999","通配符查询失败");
}
return list;
}
/**
* 模糊查询所有以 “三” 结尾的商品信息
*/
@Override
public <T> List<T> fuzzyQuery(String indexName, Class<T> beanClass, String field, String text) {
// 查询的数据列表
List<T> list = new ArrayList<>();
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
((field, text).fuzziness());
// 执行查询es数据
queryEsData(indexName, beanClass, list, searchSourceBuilder);
} catch (IOException e) {
("通配符查询失败,错误信息:" + (e));
throw new MyBusinessException("99999","通配符查询失败");
}
return list;
}
/**
* boolQuery 查询
* 高亮展示标题搜索字段
* 设置出参返回字段
*
* 案例:查询从2018-2022年间标题含 三星 的商品信息
*/
@Override
public <T> List<T> boolQuery(String indexName,Class<T> beanClass) {
// 查询的数据列表
List<T> list = new ArrayList<>();
try {
// 创建 Bool 查询构建器
BoolQueryBuilder boolQueryBuilder = ();
// 构建查询条件
(("title", "三星")); // 标题
(("spec", "联通3G"));// 说明书
().add(("createTime").format("yyyy").gte("2018").lte("2022")); // 创建时间
// 构建查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
(boolQueryBuilder);
(100);
// 甚至返回字段
// 如果查询的属性很少,那就使用includes,而excludes设置为空数组
// 如果排序的属性很少,那就使用excludes,而includes设置为空数组
String[] includes = {"title", "categoryName", "price"};
String[] excludes = {};
(includes, excludes);
// 高亮设置
// 设置高亮三要素: field: 你的高亮字段 , preTags :前缀 , postTags:后缀
HighlightBuilder highlightBuilder = new HighlightBuilder().field("title").preTags("<font color='red'>").postTags("</font>");
("spec").preTags("<font color='red'>").postTags("</font>");
(highlightBuilder);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(indexName);
(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = (searchRequest, );
// 根据状态和数据条数验证是否返回了数据
if ((()) && ().getTotalHits() > 0) {
SearchHits hits = ();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
T bean = ((), beanClass);
// 获取高亮的数据
HighlightField highlightField = ().get("title");
("高亮名称:" + ()[0].string());
// 替换掉原来的数据
Text[] fragments = ();
if (fragments != null && > 0) {
StringBuilder title = new StringBuilder();
for (Text fragment : fragments) {
(fragment);
}
// 获取method对象,其中包含方法名称和参数列表
Method setTitle = ("setTitle", );
if (setTitle != null) {
// 执行method,bean为实例对象,后面是方法参数列表;setTitle没有返回值
(bean, ());
}
}
(bean);
}
}
} catch (Exception e) {
("布尔查询失败,错误信息:" + (e));
throw new MyBusinessException("99999", "布尔查询失败");
}
return list;
}
/**
* 聚合查询 : 聚合查询一定是【先查出结果】,然后对【结果使用聚合函数】做处理.
*
* Metric 指标聚合分析。常用的操作有:avg:求平均、max:最大值、min:最小值、sum:求和等
*
* 案例:分别获取最贵的商品和获取最便宜的商品
*/
@Override
public void metricQuery(String indexName) {
try {
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = ();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
(matchAllQueryBuilder);
// 获取最贵的商品
AggregationBuilder maxPrice = ("maxPrice").field("price");
(maxPrice);
// 获取最便宜的商品
AggregationBuilder minPrice = ("minPrice").field("price");
(minPrice);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(indexName);
(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = (searchRequest, );
Aggregations aggregations = ();
ParsedMax max = ("maxPrice");
("最贵的价格:" + ());
ParsedMin min = ("minPrice");
("最便宜的价格:" + ());
} catch (Exception e) {
("指标聚合分析查询失败,错误信息:" + (e));
throw new MyBusinessException("99999", "指标聚合分析查询失败");
}
}
/**
* 聚合查询: 聚合查询一定是【先查出结果】,然后对【结果使用聚合函数】做处理.
*
* Bucket 分桶聚合分析 : 对查询出的数据进行分组group by,再在组上进行游标聚合
*
* 案例:根据品牌进行聚合查询
*/
@Override
public void bucketQuery(String indexName,String bucketField, String bucketFieldAlias) {
try {
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = ();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
(matchAllQueryBuilder);
// 根据bucketField进行分组查询
TermsAggregationBuilder aggBrandName = (bucketFieldAlias).field(bucketField);
(aggBrandName);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(indexName);
(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = (searchRequest, );
Aggregations aggregations = ();
ParsedStringTerms aggBrandName1 = (bucketField); // 分组结果数据
for ( bucket : ()) {
(() + "====" + ());
}
} catch (IOException e) {
("分桶聚合分析查询失败,错误信息:" + (e));
throw new MyBusinessException("99999", "分桶聚合分析查询失败");
}
}
/**
* 子聚合聚合查询
* Bucket 分桶聚合分析
*
* 案例:根据商品分类进行分组查询,并且获取分类商品中的平均价格
*/
@Override
public void subBucketQuery(String indexName,String bucketField, String bucketFieldAlias,String avgFiled,String avgFiledAlias) {
try {
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = ();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
(matchAllQueryBuilder);
// 根据 bucketField进行分组查询,并且获取分类信息中 指定字段的平均值
TermsAggregationBuilder subAggregation = (bucketFieldAlias).field(bucketField)
.subAggregation((avgFiledAlias).field(avgFiled));
(subAggregation);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(indexName);
(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = (searchRequest, );
Aggregations aggregations = ();
ParsedStringTerms aggBrandName1 = (bucketFieldAlias);
for ( bucket : ()) {
// 获取聚合后的 组内字段平均值,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象
ParsedAvg avgPrice = ().get(avgFiledAlias);
(() + "====" + ());
}
} catch (IOException e) {
("分桶聚合分析查询失败,错误信息:" + (e));
throw new MyBusinessException("99999", "分桶聚合分析查询失败");
}
}
/**
* 综合聚合查询
*
* 根据商品分类聚合,获取每个商品类的平均价格,并且在商品分类聚合之上子聚合每个品牌的平均价格
*/
@Override
public void subSubAgg(String indexName) {
try {
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = ();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
(matchAllQueryBuilder);
// 注意这里聚合写的位置不要写错,很容易搞混,错一个括号就不对了
TermsAggregationBuilder subAggregation = ("categoryNameAgg").field("categoryName")
.subAggregation(("categoryNameAvgPrice").field("price"))
.subAggregation(("brandNameAgg").field("brandName")
.subAggregation(("brandNameAvgPrice").field("price")));
(subAggregation);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(indexName);
(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = (searchRequest, );
//获取总记录数
("totalHits = " + ().getTotalHits());
// 获取聚合信息
Aggregations aggregations = ();
ParsedStringTerms categoryNameAgg = ("categoryNameAgg");
//获取值返回
for ( bucket : ()) {
// 获取聚合后的分类名称
String categoryName = ();
// 获取聚合命中的文档数量
long docCount = ();
// 获取聚合后的分类的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象
ParsedAvg avgPrice = ().get("categoryNameAvgPrice");
(categoryName + "======平均价:" + () + "======数量:" + docCount);
ParsedStringTerms brandNameAgg = ().get("brandNameAgg");
for ( brandeNameAggBucket : ()) {
// 获取聚合后的品牌名称
String brandName = ();
// 获取聚合后的品牌的平均价格,注意返回值不是Aggregation对象,而是指定的ParsedAvg对象
ParsedAvg brandNameAvgPrice = ().get("brandNameAvgPrice");
(" " + brandName + "======" + ());
}
}
} catch (IOException e) {
("综合聚合查询失败,错误信息:" + (e));
throw new MyBusinessException("99999", "综合聚合查询失败");
}
}
/**
* 执行es查询
* @param indexName
* @param beanClass
* @param list
* @param searchSourceBuilder
* @param <T>
* @throws IOException
*/
private <T> void queryEsData(String indexName, Class<T> beanClass, List<T> list, SearchSourceBuilder searchSourceBuilder) throws IOException {
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest(indexName);
(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = (searchRequest, );
// 根据状态和数据条数验证是否返回了数据
if ((()) && ().getTotalHits() > 0) {
SearchHits hits = ();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
Goods userInfo = ((), );
// 将 JSON 转换成对象
T bean = ((), beanClass);
(bean);
}
}
}
}
测试代码:
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import .slf4j.Slf4j;
import ;
import ;
import ;
import ;
import ;
import .;
import ;
import ;
import ;
import ;
@Slf4j
@RunWith()
@SpringBootTest
public class ElasticsearchTest1 {
@Autowired
EsQueryDataService esQueryDataService;
/**
* 单字段精确查询
*/
@Test
public void termQuery() {
// 返回数据
List<Goods> goodsList = null;
try {
goodsList = ("goods", "title", "华为", );
} catch (Exception e) {
("单字段精确查询失败,错误信息:" + (e));
}
("单字段精确查询结果:" + goodsList);
}
/**
* 单字段多内容精确查询
*/
@Test
public void termsQuery() {
// 返回数据
List<Goods> goodsList = null;
try {
String[] args = {"华为", "OPPO", "TCL"};
goodsList = ("goods", "title", args, );
} catch (Exception e) {
("单字段多内容精确查询失败,错误信息:" + (e));
}
("单字段多内容精确查询结果:" + goodsList);
}
/**
* 单字段匹配分页查询
*/
@Test
public void matchQuery() {
// 返回数据
List<Goods> goodsList = null;
try {
List<String> orderList = ("-price","-saleNum");
goodsList = ("goods", ,0,3,orderList,"title", "华为");
} catch (Exception e) {
("匹配查询失败,错误信息:" + (e));
}
("匹配查询结果:" + goodsList);
}
/**
* 单字段多内容精确查询
*/
@Test
public void matchPhraseQuery() {
// 返回数据
List<Goods> goodsList = null;
try {
goodsList = ("goods", ,"title", "华为");
} catch (Exception e) {
("词语匹配查询失败,错误信息:" + (e));
}
("词语匹配查询结果:" + goodsList);
}
/**
* 内容在多字段中进行查询
*/
@Test
public void matchMultiQuery() {
// 返回数据
List<Goods> goodsList = null;
try {
String[] fields = {"title", "categoryName"};
goodsList = ("goods", ,fields,"手机");
} catch (Exception e) {
("内容在多字段中进行查询失败,错误信息:" + (e));
}
("内容在多字段中进行查询结果:" + goodsList);
}
/**
* 通配符查询
*
* 查询所有以 “三” 结尾的商品信息
*/
@Test
public void wildcardQuery() {
// 返回数据
List<Goods> goodsList = null;
try {
goodsList = ("goods", ,"title","*三");
} catch (Exception e) {
("通配符查询查询失败,错误信息:" + (e));
}
("通配符查询结果:" + goodsList);
}
/**
* 模糊查询
*
* 模糊查询所有以 “三” 结尾的商品信息
*/
@Test
public void fuzzyQuery() {
// 返回数据
List<Goods> goodsList = null;
try {
goodsList = ("goods", ,"title","三");
} catch (Exception e) {
("模糊查询失败,错误信息:" + (e));
}
("模糊查询结果:" + goodsList);
}
@Test
public void boolQuery() {
// 返回数据
List<Goods> goodsList = null;
try {
goodsList = ("goods", );
} catch (Exception e) {
("布尔查询失败,错误信息:" + (e));
}
("布尔查询结果:" + goodsList);
}
/**
* Metric 指标聚合分析
*/
@Test
public void metricQuery() {
("goods");
}
/**
* Bucket 分桶聚合分析
*/
@Test
public void bucketQuery() {
("goods","brandName","brandNameName");
}
/**
* 子聚合聚合查询
*/
@Test
public void subBucketQuery() {
("goods","brandName","brandNameName","price","avgPrice");
}
/**
* 综合聚合查询
*/
@Test
public void subSubAgg() {
("goods");
}
}
参考文章:SpringBoot整合RestHighLevelClient案例