前言
随着 7.X 版本的到来,Type 的概念被废除,为了适应这种数据结构的改变,ES 官方从 7.15 版本开始建议使用新的 ElasticSearch Java Client。 详细可以了解:www.elastic.co/guide/en/el…
使用
有一个需求是想做es的监控,并发出告警;其中一个步骤是接收查询的参数,构造查询条件,参数类型大致为:
参数:
index(索引名)
时间范围:startTime - endTime
查询类型:精确查询,模糊查询
查询key: 对应 index中的field (可以理解为字段名的意思)
查询value: 自定义的内容
看了一遍网上的例子,写的查询条件的field都是写死的,所以自己摸索编写
实现过程:
- 我们先往 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"
}
- 查询条件
满足需求的参数条件大概为:
{
"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"
}
}
}
]
}
}
}
- 代码实现过程
- 接收参数的对象:
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();
}