在这里,我想向大家推荐一个我自己开发的项目,也就是elasticsearch-query-builder
,这个项目目前在github上已经开源,有兴趣的朋友可以去fork或者star,你的star就是对我最大的鼓励。同时,本项目长期维护和更新,我也接受并且很高兴有小伙伴向本项目pull request,或者协同开发,有兴趣的同学可以给我发邮件。
elasticsearch-query-builder
是一个非常方便构造elasticsearch
(后面简称ES) DSL 查询语句的工具包,在elasticsearch-query-builder
中,我尝试基于配置化的操作去构建ES的查询语句,并且接受外界传入参数,这极大的减少了在Java代码中构建ES查询语句的工作,并同时减少了代码量,使代码更加直观和清晰。基于使ES中DSL构造语句和Java代码分离的思想,elasticsearch-query-builder
诞生了。去GithubFork!
elasticsearch-query-builder
工程一般作为jar包为别的工程提供使用,当然,如果需要基于本项目做二次开发,这都需要将Github上克隆本项目到本地
1 | git clone https://github.com/xiaowei1118/elasticsearch-query-builder.git |
在将本项目克隆到本地后,执行mvn package
将本项目打成jar包,或者直接将本项目作为你们自己maven项目的module项目。
elastcisearch-query-builder接受配置文件(特定json格式)或者json格式的字符串配置,配置格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | { "index": "user_portrait", "type": "docs", "from": "${from}", "size": "10", "include_source": ["name","age"], //需要哪些字段 "exclude_source": ["sex"], //排除哪些字段 "query_type": "terms_level_query", "terms_level_query": { "terms_level_type": "term_query", "term_query": { "value": "${value}", "key": "key", "boost": 2 } }, "aggregations": [ { "aggregation_type": "terms", "name": "", "field": "field", "sub_aggregations": { "aggregation_type": "terms", "name": "sub", "field": "field", "size": "${size.value}", "sort": "asc", "sort_by": "_count" } } ], "highlight":{ "fields": [ { "field": "content", "number_of_fragment": 2, "no_match_size": 150 } ], "pre_tags":["<em>"], "post_tags":["</em>"] }, "sort": [ "_score", { "field": "age", "order": "asc" } ] } |
index表示ES中的索引或者别名。
type表示ES索引或者别名下的type。
from表示检索文档时的偏移量,相当于关系型数据库里的offset。
include_source 搜索结果中包含某些字段,格式为json数组,"include_source": ["name","age"]
。
exclude_source 搜索结果中排除某些字段,格式为json数组,"exclude_source":["sex"]
。
query_type表示查询类型,支持三种类型terms_level_query
,text_level_query
,bool_level_query
,并且这三种类型
不可以一起使用。
terms_level_query
操作的精确字段是存储在反转索引中的。这些查询通常用于结构化数据, 如数字、日期和枚举, 而不是全文字段,包含term_query,terms_query,range_query,exists_query 等类型。
text_level_query
查询通常用于在完整文本字段 (如电子邮件正文) 上运行全文查询。他们了解如何分析被查询的字段, 并在执行之前将每个字段的分析器 (或 search_analyzer) 应用到查询字符串。
包含 match_query,multi_match_query,query_string,simple_query_string 等类型。
bool_query
与其他查询的布尔组合匹配的文档匹配的查询。bool 查询映射到 Lucene BooleanQuery。它是使用一个或多个布尔子句生成的, 每个子句都有一个类型化的实例。 布尔查询的查询值包括: must,filter,should,must_not。 在每个布尔查询的查询类型值中, 可以包含terms_level_query 和 text_level_query中任意的查询类型,如此便可以构造非常复杂的查询情况。
terms_level查询类型,支持term_query
,terms_query
,range_query
,exists_query
查询。
term_query
1 2 3 4 5 | "term_query": { "value": "", "key": "", "boost": 2 } |
terms_query
term_query
。 1 2 3 4 | "terms_query": { "value": "[1,2]", //数组 "key": "" }, |
range_query
,给定的查询条件使一个范围
1 2 3 4 5 6 7 8 | "range_query": { "key": "", "range": "", //[,] "boost": "", "format": "", "include_lower": true, "include_upper": false } |
exists_query
,存在查询,查找字段不存在的文档。
1 2 3 | "exists_query": { "key": "" } |
text_level_query查询类型,支持match_query,multi_match_query,query_string,simple_query_string等。
match_query,普通的文本匹配查询。
1 2 3 4 5 | "match_query": { "key": "", "value": "this is a test", "zero_terms_query": "none" } |
multi_match_query 在多个字段中进行文本匹配
1 2 3 4 5 | "multi_match_query": { "value": "", "fields": "a^3,b", "type": "best_fields" //most_fields,cross_fields,phrase,phrase_prefix } |
query_string 字符串文本匹配。
1 2 3 4 | "query_string": { "value": "", "fields": ""//数组 } |
and
或者 or
。
1 2 3 4 5 | "simple_query_string": { "value": "", "fields": "", //数组 "default_operate": "and" } |
bool_query是其他查询的布尔组合,一般用于构建复杂的查询,而这正是elasticsearch-query-builder
最拿手的地方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | "query_type": "bool_query", "bool_query": [ { "bool_type": "must", "items": [ { "value": "", "key": "province_code", "terms_method": "term" //term,terms,range,match }, { "value": "", "key": "city_code", "terms_method": "term" //term,terms,range,match } ] }, { "bool_type":"filter", "items":[ "value": "", "key": "sex", "terms_method":"term" ] } ] |
ES的聚合操作通常用于聚合查询结果数据,通常用于数据的分类和统计工作。同时ES本身支持多种聚合操作,为我们的数据分析和统计提供了便利,相应的,本项目也支持聚合操作的配置化和参数绑定。
1 2 3 4 5 6 7 8 | "aggregations": [ { "aggregation_type": "avg", // 聚合类型 "name": "", //聚合的名称 "field": "", "missing_value": 10 } ] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | { "aggregation_type": "terms", "name": "", "field": "", "size": "${size.value}", "sort": "asc", "sort_by": "_count", "sub_aggregations":{ //子聚合 "aggregation_type": "terms", "name": "", "field": "", "size": "${size.value}", "sort": "asc", "sort_by": "_count" } } |
因为aggregation聚合的类型比较多,另外还有,min, max, cardinality,extended_stats, stats,sum,top_hits,value_count,range,missing,date_range,ipv4_range,date_histogram
等,这里就不再赘述,需要查看聚合类型怎么用的,可以查看配置文件样例
ES可以设置对查询结果中包含搜索关键字的字段部分进行高亮。
<em>
</em>
1 2 3 4 5 6 7 8 9 10 11 12 13 | "highlight":{ "fields": [ { "field": "content", "number_of_fragment": 2, "fragment_size": 150 } ], "pre_tags":[], "post_tags":[], "tags_schema":"", //styled "order":"score" } |
单值绑定,这里我们以sex字段为例,我们需要查询出index中性别为女性的记录,我们可以用terms查询,如:
1 2 3 4 | "term_query": { "value": "female", "key": "sex", } |
通过以上查询就可以查出性别为女性的文档。那如果我们的value值需要从外面传进来呢,比如我们的参数在一个json字符串中(非常适合application/json的传值:)),如:{ "sex": "female",type: 1}
,我们的配置文件应该怎么写?
在elasticsearch-query-builder
中,我们约定需要外界绑定的参数用${}
将字段包括进来,如:${sex}
这里的sex
同json数据里面的key一致。那么在配置文件中就转换成了:
1 2 3 4 | "term_query": { "value": "${sex}", "key": "sex", } |
如果json中的字段不在第一层呢?比方说:{ "a.sex": "female",type: 1}
, 那么我们用.
号来表示层级结构,${a.sex}
, 不管层级多深都没有问题。
对于范围类型的参数绑定,比如:type 从 1->6
或者 date 从 2017-06-10 -> 2017-12-12
, 我们应该怎么从外界进行参数绑定呢?在range查询中,我们已经定义了range的传参方式,如下:
1 2 3 4 | "range_query":{ "range": "[2017-06-10, 2017-12-12]" "key": "date" } |
其实,其实range的参数绑定和单值的参数绑定是一致的,虽然有范围,其实取的还是单值${date}
, 只是我们对外界的json数据结构表示范围的字段有限制,我们规定json中表示范围的字段必须是[a,b]
的形式,a和b可以单一缺省,表示无上界或者下界,但是a和b不可以同时缺省(同时缺省,这个范围查询是没有意义的)。
如json数据:{ "date": "[2017-06-10, 2017-12-12]" }
即符合规范。
这个例子也是elasticsearch-query-builder
种的example。
我们先定义配置文件test.json
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | { "index":"event", "query_type":"bool_query", "size":"${pageSize}", "from":"${offset}", "include_source": ["age"], "sort":[ { "field":"date_time", "order":"desc" } ], "bool_query":[ { "bool_type":"filter", "items":[ { "terms_method":"term", "value":"${is_success}", "key":"is_success" }, { "terms_method":"range", "range":"${date_time}", "key":"date_time" }, { "terms_method":"term", "value":"${id}}", "key":"id" } ] }, { "bool_type":"must", "items":[ { "terms_method":"match", "value":"${name}", "key":"name" } ] } ], "type":"docs" } |
使用elasticsearch-query-builder
生成ES的查询语句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.coryphaei.query.FileUtil; import org.coryphaei.query.parser.ZWSearchParser; import org.elasticsearch.search.builder.SearchSourceBuilder; /** * Created by twist on 2017-08-05. */ public class ElasticsearchParser { public static void main(String[] args) throws Exception { String dataStr = "{\n" + //需要绑定的json格式参数 "\t\"pageSize\":10,\n" + "\t\"from\": 10,\n" + "\t\"id\": \"1\",\n" + "\t\"date_time\":\"[2017-06-01,]\",\n" + "\t\"is_success\":1,\n" + "\t\"name\":\"Bob\"\n" + "}"; JSONObject data = JSON.parseObject(dataStr); String config = FileUtil.readResourceRemoveComments(ElasticsearchParser.class, "test.json"); //加载配置文件 SearchSourceBuilder searchSourceBuilder = ZWSearchParser.searchSourceBuilder(JSON.parseObject(config), data); //生成DSL查询语句 System.out.println(searchSourceBuilder.toString()); } } |
生成的ES的DSL查询语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | { "size" : 10, "query" : { "bool" : { "must" : { "match" : { "name" : { "query" : "Bob", "type" : "boolean" } } }, "filter" : [ { "term" : { "is_success" : "1" } }, { "range" : { "date_time" : { "from" : "2017-06-01", "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }, "_source" : { "includes" : [ "age" ], "excludes" : [ ] }, "sort" : [ { "date_time" : { "order" : "desc" } } ] } |
本项目并没有涵盖ES的所有查询功能,同时,也没有包含ES的最新版本的功能,这些都是我后续需要逐渐完善的地方,我希望可以通过自己的努力,使本项目越来越完善。
本项目使用了阿里的fastjson
jar包,elasticSearch公司的elasticsearch
jar包,以及io.searchbox
的jest
jar包,这里表示由衷的感谢。