Elasticsearch Java API Client客户端的查询操作

1,602 阅读3分钟

前言

随着 7.X 版本的到来,Type 的概念被废除,为了适应这种数据结构的改变,ES 官方从 7.15 版本开始建议使用新的 ElasticSearch Java Client。 详细可以了解:www.elastic.co/guide/en/el…

使用

有一个需求是想做es的监控,并发出告警;其中一个步骤是接收查询的参数,构造查询条件,参数类型大致为:

参数:

index(索引名)

时间范围:startTime - endTime

查询类型:精确查询,模糊查询

查询key: 对应 index中的field (可以理解为字段名的意思)

查询value: 自定义的内容

看了一遍网上的例子,写的查询条件的field都是写死的,所以自己摸索编写

实现过程:

  1. 我们先往 elasticsearch 中添加数据
POST employee/_doc/1
{
  "title" : "练习时长2个月的实习生",
  "name" : "yiyu",
  "age" : 23,
  "time" : "2022-03-23",
  "about" : "I love to go rock climbing"
}

POST employee/_doc/2
{
  "title": "攻城狮",
  "name": "jf",
  "age": 23,
  "time": "2022-01-01",
  "about": "I love coding"
}

POST employee/_doc/3
{
  "title": "弟弟",
  "name": "dd",
  "age": 23,
  "time": "2022-01-01",
  "about": "I love shuidajiao"
}
  1. 查询条件
满足需求的参数条件大概为:
{
    "index": "employee", 
    "startTime": "2022-01-01", 
    "endTime": "2022-03-23", 
    "match": {
        "name": "jf",
        "age": 23
    }, 
    "fuzzy": {
        "about": "lave"
    }
}


对应的es的查询条件为:
GET employee/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "age": 23
          }
        },
        {
         "match": {
           "name": "jf"
         } 
        },
        {
          "fuzzy": {
            "about": {
              "value": "lave"
            }
          }
        },
        {
          "range": {
            "time": {
              "from": "2022-01-01",
              "to": "2022-03-23"
            }
          }
        }
      ]  
    }
  }
}
  1. 代码实现过程
  • 接收参数的对象:

public class QueryESReq {
    private String index;

    private String startTime;

    private String endTime;

    private Map<String, String> match;

    private Map<String, String> fuzzy;
    
    getter()、setter() and toString()...
}
  • 接收结果的对象

public class Employee {
    @JsonProperty("title")
    private String title;

    @JsonProperty("name")
    private String name;

    @JsonProperty("age")
    private Integer age;

    @JsonProperty("about")
    private String about;

    @JsonProperty("time")
    private String time;
    
    getter()、setter() and toString()...
}
  • 查询过程
public class Main {
    public static void main(String[] args) {
        String str = "{\n" +
                "    "index": "employee", \n" +
                "    "startTime": "2022-01-01", \n" +
                "    "endTime": "2022-03-23", \n" +
                "    "match": {\n" +
                "        "name": "jf",\n" +
                "        "age": 23\n" +
                "    }, \n" +
                "    "fuzzy": {\n" +
                "        "about": "lave"\n" +
                "    }\n" +
                "}";
        Gson gson = new Gson();
        QueryESReq queryESReq = gson.fromJson(str, QueryESReq.class);

        // 如果没有设置密码的话,直接 RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); 即可
        
        // 登录模块
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        // es账号密码(默认用户名为elastic)
        credentialsProvider.setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials("elastic", "128628"));
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200))
                .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                    @Override
                    public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
                        httpAsyncClientBuilder.disableAuthCaching();
                        return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                    }
                })
                .build();

        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);
        try{
            Map<String, String> matchMap = queryESReq.getMatch();
            Map<String, String> fuzzyMap = queryESReq.getFuzzy();
            // 查询条件用 List<Query> 存起来
            List<Query> queryList = new ArrayList<>();

            for(String key : queryESReq.getMatch().keySet()) {
                Query query = Query.of(q -> q.match(m -> m
                        .field(key)
                        .query(FieldValue.of(matchMap.get(key)))));
                queryList.add(query);
            }

            for(String key : queryESReq.getFuzzy().keySet()) {
                Query query = Query.of(f -> f.fuzzy(m -> m
                        .field(key)
                        .value(FieldValue.of(fuzzyMap.get(key)))));
                queryList.add(query);
            }

            Query query = Query.of(q -> q.range(r -> r.
                    field("time")
                    .from(JsonData.of(queryESReq.getStartTime()))
                    .to(JsonData.of(queryESReq.getEndTime()))));
            queryList.add(query);

            SearchRequest.Builder searchBuilder = new SearchRequest.Builder();
            searchBuilder.index(queryESReq.getIndex());
            searchBuilder.query(q -> q.bool(b -> b.must(queryList)));
            SearchRequest searchRequest = searchBuilder.build();

            SearchResponse<Employee> employee = client.search(searchRequest, Employee.class);
            System.out.println(employee.took());
            System.out.println(employee.hits().total().value());
            employee.hits().hits().forEach(e -> System.out.println(e.source().toString()));
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6

1

Employee{title='攻城狮', name='jf', age=23, about='I love coding', time='2022-01-01'}

总结

一开始按照官网和别人写的例子,大概写了个查询如下:

SearchResponse<Employee> employee = client.search(s -> s
                .index("megacorp")
                .query(q -> q
                        .bool(b -> b
                                .must(must -> must
                                        .match(m -> m
                                                .field("name")
                                                .query(FieldValue.of(queryESReq.getMatch().get("name"))))
                                )
                                .must(must -> must
                                        .match(m -> m
                                                .field("age")
                                                .query(FieldValue.of(queryESReq.getMatch().get("age"))))
                                )
                                .must(must -> must
                                        .fuzzy(f -> f
                                                .field("about")
                                                .value(FieldValue.of("lave"))
                                        )
                                )
                                .must(must -> must
                                        .range(r -> r
                                                .field("time")
                                                .from(JsonData.of("2022-01-01"))
                                                .to(JsonData.of("2022-03-23"))
                                        )
                                )
                        )
                )
        , Employee.class);

但是这些field都是写死的,而需求传的index,和查询的field、value值等是不确定的,本来想放弃用回 High Level client,后来去看了一下must函数,有三种类型的参数, 看到了可以将Query类型包装成List传到must中,于是看了下 Query

BoolQuery类

public final BoolQuery.Builder must(List<Query> list) {
    this.must = _listAddAll(this.must, list);
    return this;
}

public final BoolQuery.Builder must(Query value, Query... values) {
    this.must = _listAdd(this.must, value, values);
    return this;
}

public final BoolQuery.Builder must(Function<co.elastic.clients.elasticsearch._types.query_dsl.Query.Builder, ObjectBuilder<Query>> fn) {
    return this.must((Query)((ObjectBuilder)fn.apply(new co.elastic.clients.elasticsearch._types.query_dsl.Query.Builder())).build());
}

Query类:表明可以构造查询条件并返回Query,于是解决问题。

public static Query of(Function<Query.Builder, ObjectBuilder<Query>> fn) {
    return (Query)((ObjectBuilder)fn.apply(new Query.Builder())).build();
}