Skip to content
This repository has been archived by the owner on Sep 4, 2019. It is now read-only.
/ elasticsearch-sql2 Public archive

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

Notifications You must be signed in to change notification settings

iamazy/elasticsearch-sql2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 

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 version latest version remark isql version
6.x 6.6.0 6.6.0.1
6.x 6.6.1 6.6.1.1
6.x 6.6.2 6.6.2.1
6.x 6.7.0 6.7.0.1
6.x 6.7.1 6.7.1.1
7.x 7.0.0 7.0.0.2
7.x 7.0.1 7.0.1.1
7.x 7.1.0 7.1.0.1
7.x 7.1.1 7.1.1.1
7.x 7.2.0 7.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-sql es version
master 6.6.0
master 6.6.2
master 6.7.0
master 6.7.1
master 7.0.0
master 7.0.1
master 7.1.1
master 7.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进行多用户的搜索请求 下面是官网的一段代码

import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
...

TransportClient client = new PreBuiltXPackTransportClient(Settings.builder()
        .put("cluster.name", "myClusterName")
        .put("xpack.security.user", "transport_client_user:x-pack-test-password")
        ...
        .build())
    .addTransportAddress(new TransportAddress("localhost", 9300))
    .addTransportAddress(new TransportAddress("localhost", 9301));

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

private static RequestOptions requestOptions(String token) {
        //设置允许返回的最大字节数
        HttpAsyncResponseConsumerFactory responseConsumerFactory = new HttpAsyncResponseConsumerFactory
                .HeapBufferedResponseConsumerFactory(Integer.MAX_VALUE);
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        builder.setHttpAsyncResponseConsumerFactory(responseConsumerFactory);
        builder.addHeader("Authorization", "Basic " + token);
        return builder.build();
}

public Map<String, Object> get(String cluster,String index,String type, String id, String routing, String token) throws IOException {
        GetRequest getRequest = new GetRequest(index, type, id).routing(routing);
        GetResponse getResponse = getRestHighLevelClient(cluster).get(getRequest, requestOptions(token));
        return getResponse.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

@Test
public void testParseFromMethodSource(){
   String sql="select * from fruits where match(name,'apple','prefix_length:21') and term(color,'red')";
   ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser();
   ElasticSqlParseResult parseResult = sql2DslParser.parse(sql,new String[]{"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

@Test
public void testParseLimit(){
    String sql="select * from fruits where match_phrase(name,'apple') and term(color,'red') limit 2,9";
    ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser();
    ElasticSqlParseResult parseResult = sql2DslParser.parse(sql,new String[]{"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

@Test
public void testParseTermsAgg(){
    String sql="select * from fruits where name is not null and color is not null group by terms(weight,5000),terms(color,600)";
    ElasticSql2DslParser sql2DslParser=new ElasticSql2DslParser();
    ElasticSqlParseResult parseResult = sql2DslParser.parse(sql,new String[]{"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

public static void main(String[] args) {
    String sql="DELETE from fruits where match_all() limit 1100";
    ElasticSql2DslParser elasticSql2DslParser=new ElasticSql2DslParser();
    ElasticSqlParseResult elasticSqlParseResult = elasticSql2DslParser.parse(sql, new String[]{"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