Elasticsearch能支持动态的DSL

2022-08-12 21:24:55 浏览数 (1)

背景

当我们使用了Elasticsearch,我们就需要考虑如何去访问Elasticsearch上面的数据,目前官方推荐RestHighLevelClient SDK去操作es数据,但是使用过的同学的知道,要构造这个这个请求对象很麻烦,特别是当我们的查询复杂的时候。 下面有一段本blog使用Sdk去访问es的代码,看起来就知道构造一个查询对象麻烦。

代码语言:javascript复制
RestHighLevelClient client = new RestHighLevelClient(restClient); 
// 创建并设置SearchSourceBuilder对象 
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); 
BoolQueryBuilder filterQueryBuilder = new BoolQueryBuilder(); 
if(categoryId != null && categoryId != Category.INDEX_0){//首页查询所有 
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("category_id",categoryId); 
filterQueryBuilder.must(termQueryBuilder); 
} 
filterQueryBuilder.must(QueryBuilders.termQuery("states",1));//已发布的 
//filterQueryBuilder.should(QueryBuilders.matchQuery("title",searchKey)); 
//filterQueryBuilder.should(QueryBuilders.matchQuery("summary",searchKey)); 
HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder( 
"t_article_contents",QueryBuilders.matchQuery("contents",searchKey).boost(1), ScoreMode.None); 
//filterQueryBuilder.should(hasChildQueryBuilder); 
//这里的should是放在 bool query中的原本是没有作用的只有提升相关度的作用,,,但是使用.minimumShouldMatch(1); 至少匹配一个 
boolQueryBuilder.should(QueryBuilders.matchQuery("title",searchKey).boost(5)).minimumShouldMatch(1); 
boolQueryBuilder.should(QueryBuilders.matchQuery("summary",searchKey).boost(3)).minimumShouldMatch(1); 
boolQueryBuilder.should(hasChildQueryBuilder.boost(1)).minimumShouldMatch(1); 
boolQueryBuilder.filter(filterQueryBuilder); 
searchSourceBuilder.query(boolQueryBuilder); 
searchSourceBuilder.from(0); 
// 每页多少条数据 
searchSourceBuilder.size(1000); 
// 设置排序规则 
searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC)); // 按_score降序排序(默认值) 
searchSourceBuilder.sort("publish_time", SortOrder.DESC); 
// 设置超时时间为2s 
searchSourceBuilder.timeout(new TimeValue(2000)); 
//高亮 
HighlightBuilder highlightBuilder = new HighlightBuilder();//Creates a new HighlightBuilder 
highlightBuilder.preTags("<span class="highlight-es">").postTags("</span>"); 
HighlightBuilder.Field highlightTitle = 
new HighlightBuilder.Field("title");//Create a field highlighter for the title field 
highlightTitle.highlighterType("unified"); //Set the field highlighter type 
highlightBuilder.field(highlightTitle); //Add the field highlighter to the highlight builder 
HighlightBuilder.Field highlightSummary = new HighlightBuilder.Field("summary"); 
highlightBuilder.field(highlightSummary); 
searchSourceBuilder.highlighter(highlightBuilder); 
// 创建并设置SearchRequest对象 
SearchRequest searchRequest = new SearchRequest(); 
// 设置request要搜索的索引和类型 
searchRequest.indices("blog").types("t_article"); 
searchRequest.source(searchSourceBuilder); 
SearchResponse searchResponse = client.search(searchRequest); 
System.out.printf(searchResponse.toString()); 
SearchHits hits = searchResponse.getHits(); 
List<Article> publishList = new ArrayList<>(); 
for(SearchHit hit : hits){ 
Article article = new Article(); 
Map<String, Object> sourceAsMap = hit.getSourceAsMap(); 
article.setId(Long.parseLong(hit.getId())); 
article.setTitle((String)sourceAsMap.get("title")); 
article.setThumbUrl((String)sourceAsMap.get("thumb_url")); 
article.setSummary((String)sourceAsMap.get("summary")); 
article.setClickNum(Integer.valueOf(sourceAsMap.get("click_num") "")); 
article.setPublishTime((String)sourceAsMap.get("publish_time")); 
article.setCreator((String)sourceAsMap.get("creator")); 
Map<String, HighlightField> highMap = hit.getHighlightFields(); 
if(highMap!=null && !highMap.isEmpty()){ 
if(highMap.get("title")!=null){ 
article.setTitle(highMap.get("title").getFragments()[0].string()); 
} 
if(highMap.get("summary")!=null){ 
article.setSummary(highMap.get("summary").getFragments()[0].string()); 
} 
} 
publishList.add(article); 
} 
mv.addObject("publishList",publishList); 
List<Article> mostClickList = this.findMostClickArticle(); 
mv.addObject("mostClickList",mostClickList); 
client.close(); 

使用过mybatis和hibernate的同学都知道,mybatis简单,易学,那么我们也可以使用elasticsearch的语法DSL来查询。

动态DSL

我们可以建一个文件我们的dsl放在resource下面,customer_dsl.xml

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?> 
<dsls> 
<!--统计客户的的人数--> 
<dsl id="customer.calcCustomerCount"> 
{ 
"size": 0, 
"query": { 
"bool": { 
"must": [ 
{ 
"term": { 
"type": "customer" 
} 
}, 
{ 
"term": { 
"group_id": { 
"value": "${#groupId}" 
} 
} 
}, 
。。。。 
。。。。。 
。。。。。 
。。。。。 
<if test="#brandId == null and #isCustomer != null and #isCustomer"> 
,{ 
"range": { 
"first_pay_time": { 
"gte": "${#startTimeUtc}", 
"lte": "${#endTimeUtc}" 
} 
} 
} 
</if> 
] 
} 
} 
} 
</dsl> 
</dsls> 

解析动态的dsl语句,这里我为了简单将customer_dsl,放在c盘,这个可以转成dsl,然后使用RestHighLevelClient来直接执行DSL.

代码语言:javascript复制
public static void main(String[] args) throws Exception { 
DefaultQueryDSLFactory dslFactory = new DefaultQueryDSLFactory("file:c:/customer_dsl.xml"); 
Map<String, Object> searchMap = new HashMap<>(); 
searchMap.put("groupId",123); 
String s = dslFactory.getQueryDsl("customer.calcCustomerCount", searchMap); 
System.out.println(s); 
} 

上面看到这个语法${#groupId},可能会觉得很奇怪,不像mybatis啊,这个是使用SpEl语法#groupId,然后我们又重写了前缀,和后缀的方法

代码语言:javascript复制
private final String expressionPrefix = "${"; 
private final String expressionSuffix = "}"; 
@Override 
public final String getExpressionPrefix() { 
return this.expressionPrefix; 
} 
@Override 
public final String getExpressionSuffix() { 
return this.expressionSuffix; 
} 

可以配置项目启动的创建bean会自动加载dsl.xml文件放在缓存中

代码语言:javascript复制
@Configuration 
public class DefaultQueryDSLFactoryConfig { 
@Bean 
public DefaultQueryDSLFactory createDefaultQueryDSLFactory() throws Exception { 
DefaultQueryDSLFactory defaultQueryDSLFactory = new DefaultQueryDSLFactory("classpath*:**/*_dsl.xml"); 
return defaultQueryDSLFactory; 
} 
} 

代码

0 人点赞