ES检索
1、Java操作ES
配置ES
@Configuration
public class GulimallElasticSearchConfig {
public static final RequestOptions COMMON_OPTIONS;
//RequestOptions可以配置一些请求头的,这里不设置
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 esRestClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("192.168.56.101", 9200, "http")));
return client;
}
}
①、创建RestHighLevelClient,每次操作都是RestHighLevelClient操作ES,restHighLevelClient.search(SearchRequest searchRequest, RequestOptions options)
②、创建SearchRequest (索引请求),将检索参数封装成请求
③、创建SearchSourceBuilder,构建检索语句
④、创建QueryBuilders,构建每一个子语句
⑤、将返回的数据封装,result = buildSearchResult(response, Param);
@Override
public SearchResult search(SearchParam Param) {
SearchResult result = null;
// 1.准备检索请求
SearchRequest searchRequest = buildSearchRequest(Param);
try {
// 2.执行检索请求
SearchResponse response = restHighLevelClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
// 3.分析响应数据
result = buildSearchResult(response, Param);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
2、构建ES查询语句
/**
* 准备检索请求 [构建查询语句]
*/
private SearchRequest buildSearchRequest(SearchParam Param) {
// 帮我们构建DSL语句的
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 1. 模糊匹配 过滤(按照属性、分类、品牌、价格区间、库存) 先构建一个布尔Query
// 1.1 must
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if(!StringUtils.isEmpty(Param.getKeyword())){
boolQuery.must(QueryBuilders.matchQuery("skuTitle",Param.getKeyword()));
}
// 1.2 bool - filter Catalog3Id
if(StringUtils.isEmpty(Param.getCatalog3Id() != null)){
boolQuery.filter(QueryBuilders.termQuery("catalogId", Param.getCatalog3Id()));
}
// 1.2 bool - brandId [集合]
if(Param.getBrandId() != null && Param.getBrandId().size() > 0){
boolQuery.filter(QueryBuilders.termsQuery("brandId", Param.getBrandId()));
}
// 属性查询
if(Param.getAttrs() != null && Param.getAttrs().size() > 0){
for (String attrStr : Param.getAttrs()) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
String[] s = attrStr.split("_");
// 检索的id 属性检索用的值
String attrId = s[0];
String[] attrValue = s[1].split(":");
boolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", attrId));
boolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", attrValue));
// 构建一个嵌入式Query 每一个必须都得生成嵌入的 nested 查询
NestedQueryBuilder attrsQuery = QueryBuilders.nestedQuery("attrs", boolQueryBuilder, ScoreMode.None);
boolQuery.filter(attrsQuery);
}
}
// 1.2 bool - filter [库存]
if(Param.getHasStock() != null){
boolQuery.filter(QueryBuilders.termQuery("hasStock",Param.getHasStock() == 1));
}
// 1.2 bool - filter [价格区间]
if(!StringUtils.isEmpty(Param.getSkuPrice())){
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
String[] s = Param.getSkuPrice().split("_");
if(s.length == 2){
// 有三个值 就是区间
rangeQuery.gte(s[0]).lte(s[1]);
}else if(s.length == 1){
// 单值情况
if(Param.getSkuPrice().startsWith("_")){
rangeQuery.lte(s[0]);
}
if(Param.getSkuPrice().endsWith("_")){
rangeQuery.gte(s[0]);
}
}
boolQuery.filter(rangeQuery);
}
// 把以前所有条件都拿来进行封装
sourceBuilder.query(boolQuery);
// 1.排序
if(!StringUtils.isEmpty(Param.getSort())){
String sort = Param.getSort();
// sort=hotScore_asc/desc
String[] s = sort.split("_");
SortOrder order = s[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC;
sourceBuilder.sort(s[0], order);
}
// 2.分页 pageSize : 5
sourceBuilder.from((Param.getPageNum()-1) * EsConstant.PRODUCT_PAGESIZE);
sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
// 3.高亮
if(!StringUtils.isEmpty(Param.getKeyword())){
HighlightBuilder builder = new HighlightBuilder();
builder.field("skuTitle");
builder.preTags("<b style='color:red'>");
builder.postTags("</b>");
sourceBuilder.highlighter(builder);
}
// 聚合分析
// TODO 1.品牌聚合
TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
brand_agg.field("brandId").size(50);
// 品牌聚合的子聚合
brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
// 将品牌聚合加入 sourceBuilder
sourceBuilder.aggregation(brand_agg);
// TODO 2.分类聚合
TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
// 将分类聚合加入 sourceBuilder
sourceBuilder.aggregation(catalog_agg);
// TODO 3.属性聚合 attr_agg 构建嵌入式聚合
NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
// 3.1 聚合出当前所有的attrId
TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
// 3.1.1 聚合分析出当前attrId对应的attrName
attrIdAgg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
// 3.1.2 聚合分析出当前attrId对应的所有可能的属性值attrValue 这里的属性值可能会有很多 所以写50
attrIdAgg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
// 3.2 将这个子聚合加入嵌入式聚合
attr_agg.subAggregation(attrIdAgg);
sourceBuilder.aggregation(attr_agg);
log.info("\n构建语句:->\n" + sourceBuilder.toString());
SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
return searchRequest;
}