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

2022/11/17 编程技巧 Elasticsearch 共 2470 字,约 8 分钟

背景

版本

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

目标

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

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

{
    "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).

官方示例:

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.

{
  "wrapper" : {
    "query" : "eyJzaXplIjoxLCJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19fQ=="
  }
}

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

{
  "query" : {
    "wrapper": {
      "query" : "eyJzaXplIjoxLCJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19fQ=="
    }
  }
}

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

其实仔细分析一下,很容易发现问题. 我们将base64还原,看看最后的查询DSL的样子

{
  "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重新单独拿出来处理下

{
  "query" : {
    "wrapper": {
      "query" : "eyJib29sIjp7ImZpbHRlciI6W3sidGVybXMiOnsidXNlckNvZGUiOlsiMTEwMTExIl19fSx7InRlcm0iOnsiYWRkcmVzcyI6eyJ2YWx1ZSI6ImJlaWppbmcifX19XX19"
    }
  }
}

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

聚合

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

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

java源码

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);
    }   
}

参考

文档信息

Search

    Table of Contents