赞
踩
公司项目从sring-boot2
升级到了spring-boot3
,es的服务端也跟着升级到了es8 ,而es的客户端7和服务端8 是不兼容的,
客户端es 7使用的是: elasticsearch-rest-high-level-client
es 8 升级到: elasticsearch-java
两者之间查询api的变化还是比较大的,也花了不少时间在这个修改上,所以记录下中间的切换姿势,仅供大家参考
<!--es7 版本客户端 -->
<!-- <dependency>-->
<!-- <groupId>com.baibu.platform</groupId>-->
<!-- <artifactId>ka-order-server-interface</artifactId>-->
<!-- <version>7.1.0</version>-->
<!-- </dependency>-->
<!--es8 版本客户端 -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.10.4</version>
</dependency>
参考官方文档: https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/getting-started-java.html
@Bean public ElasticsearchClient elasticsearchClient(ESProperties esProperties) { HttpHost[] httpHosts = new HttpHost[esProperties.getNodes().size()]; // 这里配置你的es服务端host for (int i = 0; i < esProperties.getNodes().size(); i++) { ESProperties.Node node = esProperties.getNodes().get(i); HttpHost httpHost = new HttpHost(node.getHost(), node.getPort(), node.getScheme()); httpHosts[i] = httpHost; } // RestClient restClient = RestClient.builder(httpHosts).setHttpClientConfigCallback(httpClientBuilder -> { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); // 这里是设置服务端账户,密码,没有可以不用 credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(esProperties.getUsername(), esProperties.getPasswd())); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return httpClientBuilder; }).build(); ElasticsearchTransport transport = new RestClientTransport( restClient, new JacksonJsonpMapper()); // And create the API client ElasticsearchClient elasticsearchClient = new ElasticsearchClient(transport); return elasticsearchClient; }
// 构建boolquery BoolQuery.Builder boolQueryBuilder = QueryBuilders.bool(); // must wildcard 模糊查询 boolQueryBuilder.must(query -> query.wildcard(t -> t.field("wildcard").value("*" + "1111"))); // terms 多个值匹配 List<String> list ; boolQueryBuilder.must(query -> query.terms(t -> t.field("terms").terms(s -> s.value( .stream().map(FieldValue::of).collect(Collectors.toList()))))); // term 匹配 boolQueryBuilder.must(query -> query.term(t -> t.field("term").value(111))); // rang 范围 查询 boolQueryBuilder.must(query -> query.range(t -> t.field("range").gte(JsonData.of("格式化的日期".replaceFirst(" ", "T"))))); // nested 嵌套查询 boolQueryBuilder.must( query -> query.nested(nestedQuery -> nestedQuery.query(wildcardQuery -> wildcardQuery.range(t -> t.field("nested.wildcardQuery").gte(JsonData.of("格式化的日期".replaceFirst(" ", "T"))))).scoreMode(ChildScoreMode.None).path("nested")) ); SearchRequest searchRequest = SearchRequest.of(s -> s // 要查询的索引名 .index(vo.getIndex()) // 查询 条件 .query(q -> q .bool(boolQueryBuilder.build()) // 分页 ).from((vo.getPageNum() - 1) * vo.getPageSize()) .size(vo.getPageSize()) // 排序字段 .sort(sorts.stream().map(sort -> SortOptions.of(a -> a.field(f -> f.field(sort.getSortColumn()).order(sort.getSortType())))).collect(Collectors.toList())) // 查询结果包含哪些字段 .source(source -> source.filter(f -> f.includes(Arrays.stream(vo.getInclude()).toList()).excludes(""))) ); // 这里可以打印es查询 Query DSL ,可以复制到es 控制台验证查询结果 log.info("ES搜索引擎分页请求参数={}", searchRequest.toString()); // 获取查询结果 返回结果是一个map ,id是key SearchResponse<Map> elasticsearchClient.search(searchRequest, Map.class) List<Long> id = searchResponse.hits().hits().stream().map(e -> e.source().get("id")).collect(Collectors.toList());
当然你也可以参考官网的方式 一个Query 一个Query 的must,个人觉得不是很方便
tring searchText = "bike"; double maxPrice = 200.0; // Search by product name Query byName = MatchQuery.of(m -> m .field("name") .query(searchText) )._toQuery(); // Search by max price Query byMaxPrice = RangeQuery.of(r -> r .field("price") .gte(JsonData.of(maxPrice)) )._toQuery(); // Combine name and price queries to search the product index SearchResponse<Product> response = esClient.search(s -> s .index("products") .query(q -> q .bool(b -> b .must(byName) .must(byMaxPrice) ) ), Product.class ); // 获取查询结果 List<Hit<Product>> hits = response.hits().hits(); for (Hit<Product> hit: hits) { Product product = hit.source(); logger.info("Found product " + product.getSku() + ", score " + hit.score()); }
// Aggregation 统计 terms 字段每个值和对应值的数量,也可以统计avg 、interval、等 Aggregation aggregation = AggregationBuilders.terms(terms -> terms.field("terms")); SearchRequest searchRequest = SearchRequest.of(s -> s.index("索引name")) // 查询条件 .query(q -> q.bool(vo.getBoolQuery())) // 聚合条件 这里 aggregation 也可以通过lambda 自定义 a -> a.histogram(h -> h.field("price").interval(50.0)) .aggregations("aggregations",aggregation)); SearchResponse searchResponse = elasticsearchClient.search(searchRequest, Map.class); // 获取统计结果 Aggregate terms = (Aggregate) searchResponse.aggregations().get("aggregations"); searchTypeList.lterms().buckets().array().forEach(e -> { long quantity = e.docCount(); String key= e.key(); });
上面的方式很繁琐,每增加一个条件都需要我们手动设置条件查询语句,我们可以通过在字段上加上自定义注解的方式 去生成对用的查询条件
主要逻辑如下: 源码放在github ,大家自取 https://github.com/Rfruelu/es-search-api-generator
/**
* 查询模式
*/
public enum EsQueryMode {
TERM,
TERMS,
WILDCARD,
RANGE,
}
/** * 通用转换 * * @author LuTshoes * @version 1.0 */ public class GeneralConvertHandler implements IConvertHandler { /** * 将注解和对象转换为BoolQuery * * @param annotation 注解 * @param o 对象 * @return 转换后的BoolQuery */ @Override public BoolQuery convert(Annotation annotation, Object o) { // 判断注解是否为GeneralConvert类型并且对象不为空 if (annotation instanceof GeneralConvert && Objects.nonNull(o)) { // 获取注解的key值 String key = ((GeneralConvert) annotation).key(); // 获取注解的查询模式 EsQueryMode mode = ((GeneralConvert) annotation).mode(); // 使用switch语句根据查询模式执行不同的逻辑 switch (mode) { case TERM: // 如果查询模式是TERM,则构建BoolQuery对象,添加term查询条件 return QueryBuilders.bool().must(t -> t.term(f -> f.field(key).value(FieldValue.of(JsonData.of(o))))).build(); case TERMS: // 如果查询模式是TERMS,并且对象是集合类型 if (o instanceof Collection) { // 将对象转换为集合 Collection<?> collection = (Collection<?>) o; // 将集合中的每个元素转换为FieldValue对象,并构建成列表 List<FieldValue> fieldValues = collection.stream().map(c -> FieldValue.of(JsonData.of(c))).collect(Collectors.toList()); // 构建BoolQuery对象,添加terms查询条件 return QueryBuilders.bool().must(t -> t.terms(f -> f.field(key).terms(v -> v.value(fieldValues)))).build(); } break; case WILDCARD: // 如果查询模式是WILDCARD,则构建BoolQuery对象,添加wildcard查询条件 return QueryBuilders.bool().must(t -> t.wildcard(f -> f.field(key).value("*" + o + "*"))).build(); case RANGE: // 如果查询模式是RANGE,并且对象是EsRangeObject类型 if (o instanceof EsRangeObject) { // 将对象转换为EsRangeObject类型 EsRangeObject rangeObject = (EsRangeObject) o; // 创建RangeQuery.Builder对象,设置查询的字段 RangeQuery.Builder range = QueryBuilders.range().field(key); // 如果EsRangeObject的from属性不为空,则添加gte查询条件 Optional.ofNullable(rangeObject.getFrom()).ifPresent(from -> range.gte(JsonData.of(from))); // 如果EsRangeObject的to属性不为空,则添加lte查询条件 Optional.ofNullable(rangeObject.getTo()).ifPresent(to -> range.lte(JsonData.of(to))); // 构建BoolQuery对象,添加range查询条件 return QueryBuilders.bool().must(range.build()._toQuery()).build(); } break; default: // 如果查询模式不匹配任何已知模式,则不执行任何操作 break; } } // 如果注解不是GeneralConvert类型或者对象为空,则返回null return null; } }
/** * @description: 通用转换 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD}) public @interface GeneralConvert { /** * 获取键值 * * @return 返回键值 */ String key(); /** * 获取当前ES查询模式 * * @return 返回当前ES查询模式 */ EsQueryMode mode(); }
@Data @Accessors(chain = true) public class LuTshoes extends AbstractEsConditionReqDto{ @GeneralConvert(key = "term", mode = EsQueryMode.TERM) private String term; @GeneralConvert(key = "terms", mode = EsQueryMode.TERMS) private List<String> terms; @GeneralConvert(key = "wildcard", mode = EsQueryMode.WILDCARD) private String wildcard; @GeneralConvert(key = "rangeObject", mode = EsQueryMode.RANGE) private EsRangeObject rangeObject; public static void main(String[] args) throws IllegalAccessException { LuTshoes luTshoes = new LuTshoes().setTerm("term").setRangeObject(new EsRangeObject().setFrom("100").setTo("200")).setWildcard("123456").setTerms(List.of("terms","2")); System.out.println(luTshoes.build()); } @Override public BoolQuery build() throws IllegalAccessException { // 也可以自己定义实现 return BoolQueryAdapter.convert(this); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。