package org.linxidev.utils;
import jakarta.annotation.Resource;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
*
* @Author: 林夕
* @Date: 2025/6/18
* @Description: ES查询工具类 (text类型: key.keyword)
*/
@Component
public class ElasticSearchUtil {
@Resource
private RestHighLevelClient esClient;
/**
* 检查索引是否存在
* @param indexName 索引名称
* @return 是否存在
* @throws IOException
* @throws InterruptedException
*/
public boolean isIndexExist(String indexName) throws IOException {
GetIndexRequest existsRequest = new GetIndexRequest();
existsRequest.indices(indexName);
return esClient.indices().exists(existsRequest, RequestOptions.DEFAULT);
}
/**
* 执行任意 SearchRequest 查询
* @param searchRequest 查询请求
* @return 搜索响应
* @throws IOException
*/
public SearchResponse search(SearchRequest searchRequest) throws IOException {
return esClient.search(searchRequest, RequestOptions.DEFAULT);
}
/**
* 获取索引的文档总数
* @param index 索引名
* @return 文档总数
* @throws IOException
*/
public long countDocuments(String index) throws IOException {
SearchRequest request = new SearchRequest(index);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.size(0);
request.source(sourceBuilder);
SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
return response.getHits().getTotalHits();
}
/**
* 内部查询构建类,支持链式调用
*/
public static class QueryBuilder {
private final SearchRequest searchRequest;
private final SearchSourceBuilder source;
private final BoolQueryBuilder bool;
/**
* 创建一个查询构建器
* @param indices 索引名称
*/
private QueryBuilder(String... indices) {
this.searchRequest = new SearchRequest(indices);
this.source = new SearchSourceBuilder();
this.bool = QueryBuilders.boolQuery();
this.source.query(bool);
this.searchRequest.source(source);
}
/**
* 创建一个查询构建器
* @param indices 索引名称
* @return 查询构建器
*/
public static QueryBuilder build(String... indices) {
return new QueryBuilder(indices);
}
/**
* 大于等于
* @param column
* @param val
* @return
*/
public QueryBuilder ge(String column, Object val) {
this.bool.must(QueryBuilders.rangeQuery(column).gte(val));
return this;
}
/**
* 小于等于
* @param column
* @param val
* @return
*/
public QueryBuilder le(String column, Object val) {
this.bool.must(QueryBuilders.rangeQuery(column).lte(val));
return this;
}
/**
* 或
* @param other
* @return
*/
public QueryBuilder or(QueryBuilder other) {
this.bool.should(other.bool);
return this;
}
/**
* 筛选
* @param other
* @return
*/
public QueryBuilder filter(QueryBuilder other) {
this.bool.filter(other.bool);
return this;
}
/**
* 非
* @param other
* @return
*/
public QueryBuilder not(QueryBuilder other) {
this.bool.mustNot(other.bool);
return this;
}
/**
* 与
* @param queryBuilder
* @return
*/
public QueryBuilder and(QueryBuilder queryBuilder) {
this.bool.must(queryBuilder.bool);
return this;
}
/**
* 或
* @param queryBuilder
* @return
*/
public QueryBuilder or(org.elasticsearch.index.query.QueryBuilder queryBuilder) {
this.bool.should(queryBuilder);
return this;
}
/**
* 最小匹配数量 与 or 联合使用
* @param num
*/
public void minimum(int num) {
this.bool.minimumShouldMatch(num);
}
/**
* 筛选
* @param queryBuilder
* @return
*/
public QueryBuilder filter(org.elasticsearch.index.query.QueryBuilder queryBuilder) {
this.bool.filter(queryBuilder);
return this;
}
/**
* 非
* @param queryBuilder
* @return
*/
public QueryBuilder not(org.elasticsearch.index.query.QueryBuilder queryBuilder) {
this.bool.mustNot(queryBuilder);
return this;
}
/**
* 模糊
* @param column
* @param val
* @return
*/
public QueryBuilder like(String column, String val) {
this.bool.must(QueryBuilders.wildcardQuery(column, "*".concat(val).concat("*")));
return this;
}
/**
* 左模糊
* @param column
* @param val
* @return
*/
public QueryBuilder leftLike(String column, String val) {
this.bool.must(QueryBuilders.wildcardQuery(column, "*".concat(val)));
return this;
}
/**
* 右模糊
* @param column
* @param val
* @return
*/
public QueryBuilder rightLike(String column, String val) {
this.bool.must(QueryBuilders.wildcardQuery(column, val.concat("*")));
return this;
}
/**
* 大于
* @param column
* @param val
* @return
*/
public QueryBuilder gt(String column, Object val) {
this.bool.must(QueryBuilders.rangeQuery(column).gt(val));
return this;
}
/**
* 小于
* @param column
* @param val
* @return
*/
public QueryBuilder lt(String column, Object val) {
this.bool.must(QueryBuilders.rangeQuery(column).lt(val));
return this;
}
/**
* 区间
* @param column
* @param from
* @param to
* @return
*/
public QueryBuilder between(String column, Object from, Object to) {
this.bool.must(QueryBuilders.rangeQuery(column).gt(from).lte(to));
return this;
}
/**
* 等于
* @param column
* @param val
* @return
*/
public QueryBuilder equals(String column, Object val) {
this.bool.must(QueryBuilders.termQuery(column, val));
return this;
}
/**
* 包含
* @param column
* @param val
* @return
*/
public QueryBuilder in(String column, Object... val) {
this.bool.must(QueryBuilders.termsQuery(column, val));
return this;
}
/**
* 包含
* @param column
* @param inList
* @return
*/
public QueryBuilder in(String column, Collection<?> inList) {
this.bool.must(QueryBuilders.termsQuery(column, inList));
return this;
}
/**
* 匹配 字段
* @param column
* @param text
* @return
*/
public QueryBuilder match(String column, Object text) {
this.bool.must(QueryBuilders.matchQuery(column, text));
return this;
}
/**
* 匹配多个字段
* @param text
* @param fields
* @return
*/
public QueryBuilder multiMatch(Object text, String... fields) {
this.bool.must(QueryBuilders.multiMatchQuery(text, fields));
return this;
}
/**
* 批量查询IDS
* @param ids
* @return
*/
public QueryBuilder ids(String... ids) {
this.bool.must(QueryBuilders.idsQuery().addIds(ids));
return this;
}
/**
* 正则
* @param field
* @param pattern
* @return
*/
public QueryBuilder regexp(String field, String pattern) {
this.bool.must(QueryBuilders.regexpQuery(field, pattern));
return this;
}
/**
* 分页
* @param pageNum
* @param pageSize
* @return
*/
public QueryBuilder page(int pageNum, int pageSize) {
this.source.from((pageNum - 1) * pageSize);
this.source.size(pageSize);
return this;
}
/**
* 分页(页码 0 开始)
* @param from
* @return
*/
public QueryBuilder from(int from) {
this.source.from(from);
return this;
}
/**
* 分页(页面大小)
* @param size
* @return
*/
public QueryBuilder size(int size) {
this.source.size(size);
return this;
}
/**
* 正序
* @param field
* @return
*/
public QueryBuilder asc(String field) {
this.source.sort(field, SortOrder.ASC);
return this;
}
/**
* 倒序
* @param field
* @return
*/
public QueryBuilder desc(String field) {
this.source.sort(field, SortOrder.DESC);
return this;
}
/**
* 获取查询语句
* @return
*/
public String toStr() {
return this.bool.toString();
}
/**
* 构建查询语句
* @return
*/
public SearchRequest build() {
return searchRequest;
}
}
/**
* 执行查询
* @param queryBuilder
* @return
* @throws IOException
*/
public SearchResponse executeSearch(QueryBuilder queryBuilder) throws IOException {
SearchRequest request = queryBuilder.build();
return search(request);
}
/**
* 使用示例(链式调用)
*/
public void main(String[] args) {
// IPage<Map<String, Object>> page = new Page<>(esLogsVo.getPageNum(), esLogsVo.getPageSize());
try {
SearchResponse response = this.executeSearch(
ElasticSearchUtil.QueryBuilder.build("esb_test")
// 1. ge
.ge("age", 20)
// 2. le
.le("age", 30)
// 3. or (QueryBuilder)
.or(QueryBuilder.build("user_index").equals("status", "active"))
// 4. filter (QueryBuilder)
.filter(QueryBuilder.build("user_index").gt("score", 80))
// 5. not (QueryBuilder)
.not(QueryBuilder.build("user_index").in("tags", "spam"))
// 6. and (QueryBuilder)
.and(QueryBuilder.build("user_index").like("name", "John"))
// 7. or (ES QueryBuilder)
.or(QueryBuilders.matchQuery("description", "test"))
// 8. filter (ES QueryBuilder)
.filter(QueryBuilders.termQuery("category", "books"))
// 9. not (ES QueryBuilder)
.not(QueryBuilders.rangeQuery("price").lt(100))
// 10. like
.like("email", "example.com")
// 11. leftLike
.leftLike("username", "admin")
// 12. rightLike
.rightLike("city", "ville")
// 13. gt
.gt("balance", 1000)
// 14. lt
.lt("discount", 0.1)
// 15. between
.between("date", "2023-01-01", "2023-12-31")
// 16. equals
.equals("gender", "male")
// 17. in (varargs)
.in("colors", "red", "blue")
// 18. in (Collection)
.in("sizes", Arrays.asList("M", "L"))
// 19. match
.match("content", "important document")
// 20. multiMatch
.multiMatch("search term", "title", "body")
// 21. ids
.ids("1", "2", "3")
// 22. regexp
.regexp("product_code", "A[0-9]{3}")
// 23. page
.page(1, 10)
// 24. from
.from(0)
// 25. size
.size(5)
// 26. asc
.asc("created_at")
// 27. desc
.desc("modified_at")
);
// 关键字查询(或条件)
//if (!ObjectUtils.isEmpty(esLogsVo.getKeyword())){
// org.elasticsearch.index.query.QueryBuilder data = QueryBuilders.matchQuery("data", esLogsVo.getKeyword());
// org.elasticsearch.index.query.QueryBuilder response = QueryBuilders.matchQuery("response", esLogsVo.getKeyword());
// query.or(data).or(response).minimum(1);
//}
SearchHits hits = response.getHits();
List<Map<String, Object>> collect = Arrays.stream(hits.getHits()).map(SearchHit::getSourceAsMap).toList();
System.out.println(collect.size());
}catch (Exception e){
e.printStackTrace();
}
}
}