java实现对es中DSL语句的封装
1、DSL语句如下:
前一篇已经根据业务分析了es的查询结构,并构建了DSL,这篇就开始用java操作es进行封装。
对于aggs的封装就不展开说了太麻烦了。
{
"query": {
"bool": {
"must": [
{
"match": {
"skuTitle": "华为"
}
}
],
"filter": [
{
"term": {
"catalogId": "225"
}
},
{
"terms": {
"brandId": [
"2",
"4"
]
}
},
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": "12"
}
}
},
{
"terms": {
"attrs.attrValue": [
"高通(Qualcomm)",
"海思(Hisilicon)"
]
}
}
]
}
}
}
},
{
"term": {
"hasStock": "true"
}
},
{
"range": {
"skuPrice": {
"gte": 0,
"lte": 6000
}
}
}
]
}
},
"sort": [
{
"skuPrice": {
"order": "desc"
}
}
],
"from": 0,
"size": 5,
"highlight": {
"fields": {
"skuTitle": {}
},
"pre_tags": "<b style='color:red'>",
"post_tags": "</b>"
},
"aggs": {
"brand_agg": {
"terms": {
"field": "brandId",
"size": 10
},
"aggs": {
"brand_name_agg": {
"terms": {
"field": "brandName",
"size": 10
}
},
"brand_img_agg":{
"terms": {
"field": "brandImg",
"size": 10
}
}
}
},
"category_agg":{
"terms": {
"field": "catalogId",
"size": 10
},
"aggs": {
"category_name_agg": {
"terms": {
"field": "catalogName",
"size": 10
}
}
}
},
"atts_agg":{
"nested": {
"path": "attrs"
},
"aggs": {
"attr_id": {
"terms": {
"field": "attrs.attrId",
"size": 10
},
"aggs": {
"attr_name_agg": {
"terms": {
"field": "attrs.attrName",
"size": 10
}
},
"attr_value_agg":{
"terms": {
"field": "attrs.attrValue",
"size": 10
}
}
}
}
}
}
}
}
2、引入es依赖之后先对es进行配置
@Configuration
public class ElasticSearchConfig {
public static final RequestOptions COMMON_OPTIONS;
static {
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
// builder.addHeader("Authorization", "Bearer " + TOKEN);
// builder.setHttpAsyncResponseConsumerFactory(
// new HttpAsyncResponseConsumerFactory
// .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
COMMON_OPTIONS = builder.build();
}
@Bean
public RestHighLevelClient esClient(){
RestClientBuilder builder = RestClient.builder(new HttpHost("47.107.235.12", 9200, "http"));
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
return restHighLevelClient;
}
}
3、开始使用
3.1 将之前的配好的注册好的组件引进来
@Autowired
private RestHighLevelClient client;
3.2 使用RestHighLevelClient操作es
//searchRes包含返回给页面的所有信息
@Override
//操作es来检索
public searchRes search(searchParams params) throws IOException {
//一、动态构建查询需要的DSL语句
searchRes res=null;
//1、构建检索请求
/**
* #整个查询DSL包含 模糊匹配(Query里的match)、过滤(按照属性、分类、品牌、价格区间、库存),排序(sort)、
* 分页(from、size)、高亮功能(highligh)
*/
SearchRequest searchRequest = new SearchRequest();
//构建DSL查询的方法
searchRequest= buildEsQueryRequest(params);
//2、执行检索请求
SearchResponse searchResponse = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
//3、分析响应数据,构建返回格式
res=responQueryResult(searchResponse);
return res;
}
返回类型searchRes是上篇文章根据业务分析锁封装的前端的查询条件,最终将es的检索和聚合条件封装完了之后再返回。
分析:整个操作过程大致分为以下几步:
//1、构建检索请求
SearchRequest searchRequest = new SearchRequest();
//2、构建检索条件,这个构建条件再最下边
searchRequest= buildEsQueryRequest(params);
//3、执行检索请求
SearchResponse searchResponse = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
//参数一:构建好了的检索条件 参数二:刚引入es包后所配置的es信息
//4、就是对结果的返回了
//searchResponse是上一篇文章所分析的封装着结果返回
res=responQueryResult(searchResponse);
return res;
3.3、封装DSL查询语句(重点!!!!!)
buildEsQueryRequest(params)这个方法根据DSL查询语句的格式进行封装
/**
* 构建DSL语句
* @return
*/
private SearchRequest buildEsQueryRequest(searchParams params) {
// SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
SearchRequest searchRequest = new SearchRequest();
//查询索引
searchRequest.indices(EsConstant.PRODUCT_INDEX);
//SearchSourceBuilder相当于请求体,最终将所构造好的条件放到里面
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//【查询部分:模糊匹配(Query里的match)、过滤(按照属性、分类、品牌、价格区间、库存)】
/* 1、构建boolquery- QueryBuilders:es里各种查询方法的构造器,使用QueryBuilders来构建QueryBuilder */
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//1.1、构建must的模糊匹配
if(!StringUtils.isEmpty(params.getKeyword())){
//query->bool->must->match
boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",params.getKeyword()));
}
//1.2、构建filter
if(params.getCatalog3Id()!=null){
//1.2.1 根据三级分类查询
boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",params.getCatalog3Id()));
}
if(params.getBrandId()!=null&¶ms.getBrandId().size()>0){
//1.2.2 根据品牌查询
//query->bool->filter->term/terms
boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",params.getBrandId()));
}
//1.2.3 按照所有属性查询
if(params.getAttrs()!=null&¶ms.getAttrs().size()>0){
//attrs=1_安卓:苹果&attrs=2_5.56英寸及以上:5.8英寸&attrs=3_以旧换新:全新 (前边属性id,后边属性值多个属性值用:分割)
//构建嵌入式query,
//遍历attrs并将attrs里的每一个属性值都构建成nested并且将attrs封装成attrs=1_安卓:苹果这种类型
for (String attrStr : params.getAttrs()) {
BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();
String[] s = attrStr.split("_");
String attrId=s[0];//属性id
String[] attrValue=s[1].split(":"); //属性值
//query->bool->filter->nested->query->bool-must->term/terms
nestedBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId",attrId));
nestedBoolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue",attrValue));
NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQueryBuilder, ScoreMode.None);
//将nestedQuery放到query->bool->filter里边组成query->bool->filter->nested
boolQueryBuilder.filter(nestedQuery)
}
}
//1.2.4 按照库存查询
//query->bool->filter->term
boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",params.getHasStock()==1));
//1.2.5 按照价格区间查询
//skuPrice=1_500/_500/500_
/**
* "range": {
* "skuPrice": {
* "gte": 0,
* "lte": 6000
* }
* }
*/
if(!StringUtils.isEmpty(params.getSkuPrice())) {
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
//1_500按照_分割,变成[1,500]
String[] s = params.getSkuPrice().split("_");
if(s.length==2){
//区间
rangeQuery.gte(s[0]).lte(s[1]);
}else if (s.length==1){ //_500/500_ 这两种情况
//如果开头是“_”的处理
if(params.getSkuPrice().startsWith("_")){
rangeQuery.lte(s[0]);
}
//如果结尾是“_”的处理
if(params.getSkuPrice().endsWith("_")){
rangeQuery.gte(s[0]);
}
}
//query->bool->filter->range
boolQueryBuilder.filter(rangeQuery);
}
//调用查询,将所有封装好的查询条件放到"请求体"中
sourceBuilder.query(boolQueryBuilder);
//【排序(sort)、 分页(from、size)、高亮功能(highligh)】
/* 2、排序*/
if(!StringUtils.isEmpty(params.getSort())){
//sort=saleCount_desc/asc 按照销量排序
String sort=params.getSort();
String[] s = sort.split("_");
SortOrder order= s[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;
sourceBuilder.sort(s[0],order);
}
/* 3、分页 PageSize=5*/
//pageNum=1 from:0 size:5 [0,1,2,3,4]
//pageNum=2 from:5 size:5
//from=(pageNum-1)*size
sourceBuilder.from((params.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);
sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
/* 4、高亮 */
//高亮存在的意义:当前端传来keyword的时候也就是模糊匹配的时候在高亮
if(!StringUtils.isEmpty(params.getKeyword())){
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("skuTitle"); //高亮哪个属性
highlightBuilder.preTags("<b style='color:red'>");
highlightBuilder.postTags("</b>");
sourceBuilder.highlighter(highlightBuilder);
}
/* 【聚合分析】 */
//将所有构建好的DSL聚合起来,给searchRequest
searchRequest.source(sourceBuilder);
return searchRequest;
}
总体思路:
总体思路还是对照着所构建好的DSL查询语句,一一对应进行封装。
首先还是构建SearchRequest searchRequest = new SearchRequest();
在指定查询索引searchRequest.indices(EsConstant.PRODUCT_INDEX);
用SearchSourceBuilder来构DSL
//SearchSourceBuilder构建创建检索条件,DSL语句
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
主要使用QueryBuilders来操作es中的检索方法,比如
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
然后再从bool里继续嵌套,只要用到es中的检索方法就使用QueryBuilders来调用。
以下是继续从BoolQueryBuilder调用must方法,再从must方法里边调用QueryBuilders.matchQuery这样就完成了第一个。一定要明确好DSL的层级关系,才能顺利完成嵌套。
//1.1、构建must的模糊匹配
if(!StringUtils.isEmpty(params.getKeyword())){
boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",params.getKeyword()));
}
最终将构建好的查询条件拿过来。
//将所有封装好的查询条件聚合起来
sourceBuilder.query(boolQueryBuilder);
sourceBuilder会有很多,比如:sourceBuilder.sort、sourceBuilder.highlighter、sourceBuilder.size、sourceBuilder.from等
全部DSL条件都构建好了,直接将sourceBuilder全部聚合起来给searchRequest
//将所有构建好的DSL聚合起来,给searchRequest
searchRequest.source(sourceBuilder);
//最终再返回searchRequest
return searchRequest;