es在java工程中如何支持自定义的json格式的DSL

背景

版本

  • Elasticsearch : v 6.6
  • elasticsearch-rest-high-level-client.jar : v 6.6.2

目标

想提供一个web接口, 入参就是es的查询DSL json. 然后服务器用这个DSL透传转发到Elasticsearch服务期, 将数据返回给接口.
简单来说, 就是想在自己服务器上实现类似kibana上查询Elasticsearch数据的功能.

比如, 我们正常的查询DSL如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"size":1,
"query":{
"bool":{
"filter":[
{
"terms":{
"userCode":[
"110111"
]
}
},
{
"term":{
"address":{
"value":"beijing"
}
}
}
]
}
}
}

实现

查询

如果是单纯的查询, 官方提供了一个透传查询DSL的实现类 QueryBuilders.wrapperQuery(), 可以通过这个QueryBuilders来实现我们自定义DSL的透传.
让我们先看看官方怎么说

A query that accepts any other query as base64 encoded string.
This query is more useful in the context of the Java high-level REST client or transport client to also accept queries as json formatted string. In these cases queries can be specified as a json or yaml formatted string or as a query builder (which is a available in the Java high-level REST client).

官方示例:

1
2
3
4
5
6
7
8
9
curl -X GET "localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query" : {
"wrapper": {
"query" : "eyJ0ZXJtIiA6IHsgInVzZXIiIDogIktpbWNoeSIgfX0="
}
}
}
'

由此可见, 官方是将wrapper当做一个关键字类型来处理的, 将我们的查询DSL转成base64,交给es服务器解析处理.
好,下面就是我们将上述DSL 通过QueryBuilders.wrapperQuery()处理后的sql.

1
2
3
4
5
{
"wrapper" : {
"query" : "eyJzaXplIjoxLCJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19fQ=="
}
}

大家可以找个base64解码网站处理一下, 发现和我们的DSL一样. 然后用官方的方式处理下

1
2
3
4
5
6
7
{
"query" : {
"wrapper": {
"query" : "eyJzaXplIjoxLCJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19fQ=="
}
}
}

唉, 发现查询报错了, 为什么呢?
size malformed

其实仔细分析一下,很容易发现问题. 我们将base64还原,看看最后的查询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
{
"query":{
"wrapper":{
"query":{
"size":1,
"query":{
"bool":{
"filter":[
{
"terms":{
"userCode":["110111"]
}
},
{
"term":{
"address":{"value":"beijing"}
}
}
]
}
}
}
}
}
}

发现没, query里套query, 明显查询有问题. 其实这里es解析的时候, 发现wrapper关键字, 就会将下面的query查询替换原始query.

所以我们要将wrapper的不是原始完整的DSL,而是query下面的DSL. 我们将bool关键字的JSON重新单独拿出来处理下

1
2
3
4
5
6
7
{
"query" : {
"wrapper": {
"query" : "eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19"
}
}
}

搞定, 把上述DSL放到任何一个es的web客户端查询, 都有效.

聚合

关于聚合的功能, 官方没有提供现成的解析方法.
不过我觉得, 既然es的web端可以调用任意DSL, 通过java肯定能, 大不了就跳过RestHighLevelClient, 在RestLowLevelClient上想办法.

// todo 等我找到合适的办法再补充.

java源码

1
2
3
4
5
6
7
8
9
10
11
class A {
public void search(String boolJson) throws IOException {

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// query dsl 包装
searchSourceBuilder.query(QueryBuilders.wrapperQuery(boolJson));
searchSourceBuilder.size(1);

Optional<SearchResponse> response = restHighLevelClient.search(searchSourceBuilder);
}
}

参考


es在java工程中如何支持自定义的json格式的DSL
https://www.hancher.top/2022/11/17/es-query-dsl-wrapper/
作者
寒澈
发布于
2022年11月17日
许可协议