希望长大对我而言,是可以做更多想做的事,而不是被迫做更多不想做的事...... 首页 Elasticsearch能支持动态的DSL 丁D 学无止境 2020-12-09 14:46 47349已阅读 Elasticsearch DSL 摘要本文介绍如何让Elasticsearch能像Mybatis一样支持动态的SQL. ### 背景 当我们使用了Elasticsearch,我们就需要考虑如何去访问Elasticsearch上面的数据,目前官方推荐RestHighLevelClient SDK去操作es数据,但是使用过的同学的知道,要构造这个这个请求对象很麻烦,特别是当我们的查询复杂的时候。 下面有一段本blog使用Sdk去访问es的代码,看起来就知道构造一个查询对象麻烦。 ```java 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("").postTags(""); 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 publishList = new ArrayList<>(); for(SearchHit hit : hits){ Article article = new Article(); Map 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 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 mostClickList = this.findMostClickArticle(); mv.addObject("mostClickList",mostClickList); client.close(); ``` 使用过mybatis和hibernate的同学都知道,mybatis简单,易学,那么我们也可以使用elasticsearch的语法DSL来查询。 ### 动态DSL 我们可以建一个文件我们的dsl放在resource下面,customer_dsl.xml ```xml { "size": 0, "query": { "bool": { "must": [ { "term": { "type": "customer" } }, { "term": { "group_id": { "value": "${#groupId}" } } }, 。。。。 。。。。。 。。。。。 。。。。。 ,{ "range": { "first_pay_time": { "gte": "${#startTimeUtc}", "lte": "${#endTimeUtc}" } } } ] } } } ``` 解析动态的dsl语句,这里我为了简单将customer_dsl,放在c盘,这个可以转成dsl,然后使用RestHighLevelClient来直接执行DSL. ```java public static void main(String[] args) throws Exception { DefaultQueryDSLFactory dslFactory = new DefaultQueryDSLFactory("file:c:/customer_dsl.xml"); Map searchMap = new HashMap<>(); searchMap.put("groupId",123); String s = dslFactory.getQueryDsl("customer.calcCustomerCount", searchMap); System.out.println(s); } ``` 上面看到这个语法${#groupId},可能会觉得很奇怪,不像mybatis啊,这个是使用SpEl语法#groupId,然后我们又重写了前缀,和后缀的方法 ```java 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文件放在缓存中 ```java @Configuration public class DefaultQueryDSLFactoryConfig { @Bean public DefaultQueryDSLFactory createDefaultQueryDSLFactory() throws Exception { DefaultQueryDSLFactory defaultQueryDSLFactory = new DefaultQueryDSLFactory("classpath*:**/*_dsl.xml"); return defaultQueryDSLFactory; } } ``` [代码](https://github.com/348786639/dynamicDSL.git "代码") 很赞哦! (0) 上一篇:liunx磁盘扩容 下一篇:Shell入门 目录 点击排行 Elasticsearch6.3.2之x-pack redis哨兵 2019-07-09 22:05 Redis+Twemproxy+HAProxy+Keepalived 2019-07-12 17:20 GC优化策略和相关实践案例 2019-10-10 10:54 JVM垃圾回收器 2019-10-10 10:23 标签云 Java Spring MVC Mybatis Ansible Elasticsearch Redis Hive Docker Kubernetes RocketMQ Jenkins Nginx 友情链接 郑晓博客 佛布朗斯基 凉风有信 MarkHoo's Blog 冰洛博客 南实博客 Rui | 丁D Java研发工程师 生活可以用「没办法」三个字概括。但别人的没办法是「腿长,没办法」、「长得好看,没办法」、「有才华,没办法」。而你的没办法,是真的没办法。 请作者喝咖啡
publishList = new ArrayList<>(); for(SearchHit hit : hits){ Article article = new Article(); Map 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 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 mostClickList = this.findMostClickArticle(); mv.addObject("mostClickList",mostClickList); client.close(); ``` 使用过mybatis和hibernate的同学都知道,mybatis简单,易学,那么我们也可以使用elasticsearch的语法DSL来查询。 ### 动态DSL 我们可以建一个文件我们的dsl放在resource下面,customer_dsl.xml ```xml { "size": 0, "query": { "bool": { "must": [ { "term": { "type": "customer" } }, { "term": { "group_id": { "value": "${#groupId}" } } }, 。。。。 。。。。。 。。。。。 。。。。。 ,{ "range": { "first_pay_time": { "gte": "${#startTimeUtc}", "lte": "${#endTimeUtc}" } } } ] } } } ``` 解析动态的dsl语句,这里我为了简单将customer_dsl,放在c盘,这个可以转成dsl,然后使用RestHighLevelClient来直接执行DSL. ```java public static void main(String[] args) throws Exception { DefaultQueryDSLFactory dslFactory = new DefaultQueryDSLFactory("file:c:/customer_dsl.xml"); Map searchMap = new HashMap<>(); searchMap.put("groupId",123); String s = dslFactory.getQueryDsl("customer.calcCustomerCount", searchMap); System.out.println(s); } ``` 上面看到这个语法${#groupId},可能会觉得很奇怪,不像mybatis啊,这个是使用SpEl语法#groupId,然后我们又重写了前缀,和后缀的方法 ```java 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文件放在缓存中 ```java @Configuration public class DefaultQueryDSLFactoryConfig { @Bean public DefaultQueryDSLFactory createDefaultQueryDSLFactory() throws Exception { DefaultQueryDSLFactory defaultQueryDSLFactory = new DefaultQueryDSLFactory("classpath*:**/*_dsl.xml"); return defaultQueryDSLFactory; } } ``` [代码](https://github.com/348786639/dynamicDSL.git "代码")
mostClickList = this.findMostClickArticle(); mv.addObject("mostClickList",mostClickList); client.close(); ``` 使用过mybatis和hibernate的同学都知道,mybatis简单,易学,那么我们也可以使用elasticsearch的语法DSL来查询。 ### 动态DSL 我们可以建一个文件我们的dsl放在resource下面,customer_dsl.xml ```xml { "size": 0, "query": { "bool": { "must": [ { "term": { "type": "customer" } }, { "term": { "group_id": { "value": "${#groupId}" } } }, 。。。。 。。。。。 。。。。。 。。。。。 ,{ "range": { "first_pay_time": { "gte": "${#startTimeUtc}", "lte": "${#endTimeUtc}" } } } ] } } } ``` 解析动态的dsl语句,这里我为了简单将customer_dsl,放在c盘,这个可以转成dsl,然后使用RestHighLevelClient来直接执行DSL. ```java public static void main(String[] args) throws Exception { DefaultQueryDSLFactory dslFactory = new DefaultQueryDSLFactory("file:c:/customer_dsl.xml"); Map searchMap = new HashMap<>(); searchMap.put("groupId",123); String s = dslFactory.getQueryDsl("customer.calcCustomerCount", searchMap); System.out.println(s); } ``` 上面看到这个语法${#groupId},可能会觉得很奇怪,不像mybatis啊,这个是使用SpEl语法#groupId,然后我们又重写了前缀,和后缀的方法 ```java 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文件放在缓存中 ```java @Configuration public class DefaultQueryDSLFactoryConfig { @Bean public DefaultQueryDSLFactory createDefaultQueryDSLFactory() throws Exception { DefaultQueryDSLFactory defaultQueryDSLFactory = new DefaultQueryDSLFactory("classpath*:**/*_dsl.xml"); return defaultQueryDSLFactory; } } ``` [代码](https://github.com/348786639/dynamicDSL.git "代码")