Skip to content
This repository was archived by the owner on Sep 4, 2019. It is now read-only.

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

134 Commits

Repository files navigation

基于Elasticsearch的Java Rest High Level Client的elasticsearch-sql查询组件【STALLED】

PLEASE MOVE TO elasticsearch-sql

说明

这个版本的elasticsearch-sql是基于Alibaba的Druid中的SqlParser组件,由于elasticsearch许多功能并不适合使用sql表达,导致实现elasticsearch一些高级功能的时候越来越受到SqlParser的限制,所以这个版本的elasticsearch-sql仅供交流,现已另启一个项目👉elasticsearch-sql,使用Antlr4来解析sql生成dsl,使elasticsql完全可控,并将提供多语言的支持,尽请期待!

目录

使用文档

elasticsearch-sql-wiki

<dependency>
<groupId>io.github.iamazy.elasticsearch.dsl</groupId>
<artifactId>elasticsearch-sql</artifactId>
<version>${isql-version}</version>
</dependency>

插件(isql)

版本

elasticsearch versionlatest versionremarkisql version
6.x6.6.06.6.0.1
6.x6.6.16.6.1.1
6.x6.6.26.6.2.1
6.x6.7.06.7.0.1
6.x6.7.16.7.1.1
7.x7.0.07.0.0.2
7.x7.0.17.0.1.1
7.x7.1.07.1.0.1
7.x7.1.17.1.1.1
7.x7.2.07.2.0.1

安装

Elasticsearch {6.x,7.x}

./bin/elasticsearch-plugin install https://github.com/iamazy/elasticsearch-sql/releases/download/{isql-version}/elasticsearch-sql-plugin-{isql-version}.zip

使用

1. 使用sql语句直接查询elasticsearch里面的数据集
POST _isql
{
"sql":"select * from fruit"
}
语法:desc [index]
POST _isql
{
"sql":"desc fruit"
}
语法:desc [index]/[field]
POST _isql
{
"sql":"desc fruit/name"
}
2. 将sql解析成elasticsearch的dsl
POST _isql/_explain
{
"sql":"select * from fruit"
}

CHANGELOG

2019-3-6:修复原版Nested类型的nested path识别错误的问题
2019-3-7:删除了大部分无用的代码,添加了geo_distance聚类方法
2019-3-25: 聚类使用递归实现添加多层嵌套聚类方式([>]表示嵌套聚类[,]表示同级聚类),具体用法见test目录
2019-3-26: 添加scroll id深度分页
2019-3-28: 更新nested功能,支持双层嵌套类型(再多就要考虑数据结构是否合理了)
2019-4-8: 添加高亮显示
2019-4-11: 添加Function Score
2019-4-24: 将elasticsearch-sql添加为elasticsearch插件
2019-4-28: 添加like not like 查询
2019-5-5: 添加desc语法获取index(或者index/field)的mapping,无法直接获取实际的mapping,必须结合restClient使用,且desc后面只能加一个index的名称
2019-5-8: 添加excludes字段(在字段前加[^])
2019-6-5: 解决了索引名带中划线[-]报错的bug

CHANGELOG

版本

elasticsearch-sqles version
master6.6.0
master6.6.2
master6.7.0
master6.7.1
master7.0.0
master7.0.1
master7.1.1
master7.2.0

感谢

首先感谢elasticsearch-query-toolkit的作者gitchennan,elasticsearch-sql基于gitchennanelasticsearch-query-toolkit,并在其中稍作修改和添加部分功能,再次感谢gitchennan哈哈哈哈哈
gitchennan的github地址:elasticsearch-query-toolkit

介绍

elasticsearch-sql是一个基于sql查询的elasticsearch编程工具包,支持sql生成elasticsearch dsl,去掉了elasticsearch-query-toolkit中与Spring,Mybatis 集成的部分,有需要的话请参照elasticsearch-query-toolkit

特点

1)elasticsearch-sql是基于Java Rest High Level Client构建elasticsearch查询的,支持elasticsearch原生rest client调用以及第三方http请求
2)基于 alibaba的Druid数据连接池的SqlParser组件,解析sql速度快,自定义解析规则更方便
3)方便鉴权

抛弃elasticsearch传统的transport连接方式改用rest high level连接方式不仅仅是因为官方建议,而是在结合x-pack组件进行鉴权的时候更加方便 本人不知道在transport连接方式中如何复用transport client进行多用户的搜索请求 下面是官网的一段代码

importorg.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
...
TransportClientclient = newPreBuiltXPackTransportClient(Settings.builder()
.put("cluster.name", "myClusterName")
.put("xpack.security.user", "transport_client_user:x-pack-test-password")
...
.build())
.addTransportAddress(newTransportAddress("localhost", 9300))
.addTransportAddress(newTransportAddress("localhost", 9301));

每一个transport client都需要将用户名和密码写死在配置里面,如果要使用多用户进行请求的话,就不得不建立多个transport client连接,这种方式感觉很傻,尽管我之前写过用common-pool2组件管理transport client连接,但是一想到以后会有成千上万的用户访问就要建立成千上万的连接,想想都要爆炸💥
但是rest high level client就没有这个问题,它通过RequestOptions携带用户的token信息进行搜索请求,不同的用户搜索只需要分配不同的RequestOptions就行了

privatestaticRequestOptionsrequestOptions(Stringtoken) {
//设置允许返回的最大字节数HttpAsyncResponseConsumerFactoryresponseConsumerFactory = newHttpAsyncResponseConsumerFactory
.HeapBufferedResponseConsumerFactory(Integer.MAX_VALUE);
RequestOptions.Builderbuilder = RequestOptions.DEFAULT.toBuilder();
builder.setHttpAsyncResponseConsumerFactory(responseConsumerFactory);
builder.addHeader("Authorization", "Basic " + token);
returnbuilder.build();
}
publicMap<String, Object> get(Stringcluster,Stringindex,Stringtype, Stringid, Stringrouting, Stringtoken) throwsIOException {
GetRequestgetRequest = newGetRequest(index, type, id).routing(routing);
GetResponsegetResponse = getRestHighLevelClient(cluster).get(getRequest, requestOptions(token));
returngetResponse.getSourceAsMap();
}

简单又方便,简直不要太棒了好吧
🐷也许是我的使用问题,如果有人知道如何使用transport client进行多用户的搜索请求,麻烦告诉我一下,我还挺好奇的

功能点

我是向https://github.com/NLPchina/elasticsearch-sql的开发团队看齐的,功能点会慢慢的一点一点的添加的

elasticsearch-query-toolkit已有的功能

  • SQL Select

  • SQL Where

  • SQL Order by (Asc & Desc)

  • SQL Group by (Terms & Range)

  • SQL And & Or

  • SQL In

  • SQL Between And

  • SQL Is

  • SQL Not

  • SQL Null

  • SQL Nvl

  • SQL Max

  • SQL Min

  • SQL Sum

  • SQL Avg

  • SQL > & < & >= & <=

  • ES FullText

  • ES Match

  • ES MultiMatch

  • ES QueryString

  • ES SimpleQueryString

  • ES HasParent

  • ES HasChild

  • ES Join

  • ES Script

  • ES Fuzzy

  • ES Prefix

  • ES Regex

  • ES Term

  • ES Wildcard

  • ES Routing

  • ES Nested

  • ES Include[fields]

  • ES From

  • ES Size

  • ES Range(Number,Date)

elasticsearch-sql 新增的功能

  • ES MatchAll
  • ES MatchPhrase
  • ES MatchPhrasePrefix
  • ES DeleteByQuery
  • ES Cardinality (目前不支持Script的方式)
  • ES TopHits
  • ES Nested (elasticsearch-query-toolkit中nested表达方式不合理,已修正)
  • ES GeoDistance
  • 支持嵌套深层聚类
  • ES Scroll Id
  • ES 支持双层嵌套查询(nested(nested))现在以及以后也不会支持三层以上的嵌套查询
  • ES Highlighter
  • ES Boosting
  • ES Function Score
  • SQL Like
  • SQL Desc
  • ES Excludes

未来将要添加的功能

  • ES Highlighter
  • elasticsearch-sql[NLPChina]组件中我未添加的功能!!!

☀️未来的想法是将功能完善的跟NLPChina团队一样多嘻嘻

测试用例

提供几个SQL转DSL的例子(在源码test文件夹里),其他部分你们需要去elasticsearch-query-toolkit了解,或者自己看源码(推荐,原作者的代码很优秀)

1. Match

@TestpublicvoidtestParseFromMethodSource(){
Stringsql="select * from fruits where match(name,'apple','prefix_length:21') and term(color,'red')";
ElasticSql2DslParsersql2DslParser=newElasticSql2DslParser();
ElasticSqlParseResultparseResult = sql2DslParser.parse(sql,newString[]{"name","color"});
System.out.println(parseResult.toPrettyDsl(parseResult.toRequest()));
}
{
"from" : 0,
"size" : 15,
"query" : {
"bool" : {
"filter" : [ {
"bool" : {
"must" : [ {
"match" : {
"name" : {
"query" : "apple",
"operator" : "OR",
"prefix_length" : 21,
"max_expansions" : 50,
"fuzzy_transpositions" : true,
"lenient" : false,
"zero_terms_query" : "NONE",
"auto_generate_synonyms_phrase_query" : true,
"boost" : 1.0
}
}
}, {
"term" : {
"color" : {
"value" : "red",
"boost" : 1.0
}
}
} ],
"adjust_pure_negative" : true,
"boost" : 1.0
}
} ],
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
}

2. MatchPhrase,Term,Limit

@TestpublicvoidtestParseLimit(){
Stringsql="select * from fruits where match_phrase(name,'apple') and term(color,'red') limit 2,9";
ElasticSql2DslParsersql2DslParser=newElasticSql2DslParser();
ElasticSqlParseResultparseResult = sql2DslParser.parse(sql,newString[]{"name","color"});
System.out.println(parseResult.toPrettyDsl(parseResult.toRequest()));
}
{
"from" : 2,
"size" : 9,
"query" : {
"bool" : {
"filter" : [ {
"bool" : {
"must" : [ {
"match_phrase" : {
"name" : {
"query" : "apple",
"slop" : 0,
"zero_terms_query" : "NONE",
"boost" : 1.0
}
}
}, {
"term" : {
"color" : {
"value" : "red",
"boost" : 1.0
}
}
} ],
"adjust_pure_negative" : true,
"boost" : 1.0
}
} ],
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
}

🐷Term(a,b) 也可以在SQL中直接写成a='b'

3)Terms Agg

@TestpublicvoidtestParseTermsAgg(){
Stringsql="select * from fruits where name is not null and color is not null group by terms(weight,5000),terms(color,600)";
ElasticSql2DslParsersql2DslParser=newElasticSql2DslParser();
ElasticSqlParseResultparseResult = sql2DslParser.parse(sql,newString[]{"name","color"});
System.out.println(parseResult.toPrettyDsl(parseResult.toRequest()));
}
"from" : 0,
"size" : 15,
"query" : {
"bool" : {
"filter" : [ {
"bool" : {
"must" : [ {
"exists" : {
"field" : "name",
"boost" : 1.0
}
}, {
"exists" : {
"field" : "color",
"boost" : 1.0
}
} ],
"adjust_pure_negative" : true,
"boost" : 1.0
}
} ],
"adjust_pure_negative" : true,
"boost" : 1.0
}
},
"aggregations" : {
"weight" : {
"terms" : {
"field" : "weight",
"size" : 5000,
"shard_size" : 10000,
"min_doc_count" : 1,
"shard_min_doc_count" : 1,
"show_term_doc_count_error" : false,
"order" : [ {
"_count" : "desc"
}, {
"_key" : "asc"
} ]
}
},
"color" : {
"terms" : {
"field" : "color",
"size" : 600,
"shard_size" : 1200,
"min_doc_count" : 1,
"shard_min_doc_count" : 1,
"show_term_doc_count_error" : false,
"order" : [ {
"_count" : "desc"
}, {
"_key" : "asc"
} ]
}
}
}
}

4. Delete

publicstaticvoidmain(String[] args) {
Stringsql="DELETE from fruits where match_all() limit 1100";
ElasticSql2DslParserelasticSql2DslParser=newElasticSql2DslParser();
ElasticSqlParseResultelasticSqlParseResult = elasticSql2DslParser.parse(sql, newString[]{"port"});
System.out.println(elasticSqlParseResult.toPrettyDsl(elasticSqlParseResult.toDelRequest().getSearchRequest()));
}
{
"size" : 1000,
"query" : {
"bool" : {
"must" : [ {
"match_all" : {
"boost" : 1.0
}
} ],
"adjust_pure_negative" : true,
"boost" : 1.0
}
},
"_source" : false
}

🐷 DSL里的size=1000和Java中的limit 1100含义不一样 size=1000 是DeleteByQueryRequest中的SearchRequest的Size,默认为1000 limit 1100 设置的是DeleteByQueryRequest的Size,只是在DSL中没有显示

5. Nested

为了表征nested path这个属性,采用 $ 符号指明
nested path必须以 $为nested类型的属性之前结尾(非常重要)
🐖:一个嵌套表达式最多包含2个$符号

重要:productapple为例,applenested类型,则查询时的nested path应该为product.apple 以下两种写法均正确

product$apple.name

下面这几种写法错误

$product$apple.name
product.apple$name
$product.apple$name
$product$apple$name
product$apple$name

Nested结构参照

"product" : {
"properties" : {
"apple" : {
"type" : "nested",
"properties" : {
"name" : {
"type" : "text"
},
"price" : {
"type" : "double"
}
}
}
}
}

🌹其余的请去test目录下找吧

欢迎大家提issue

About

基于Java Rest High Level Client的Elasticsearch-Sql组件【stalled】

Topics

Resources

Stars

80 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages