elasticsearch-java api 8 升级

es client api 升级

背景

公司项目从sring-boot2 升级到了spring-boot3 ,es的服务端也跟着升级到了es8 ,而es的客户端7和服务端8 是不兼容的,

客户端es 7使用的是: elasticsearch-rest-high-level-client

es 8 升级到: elasticsearch-java

两者之间查询api的变化还是比较大的,也花了不少时间在这个修改上,所以记录下中间的切换姿势,仅供大家参考

升级过程

依赖调整

xml 复制代码
			 <!--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>

查询api代码调整

参考官方文档: https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/getting-started-java.html

创建client
java 复制代码
@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;
    }
查询api
java 复制代码
  
// 构建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,个人觉得不是很方便

java 复制代码
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());
}
聚合统计
java 复制代码
// 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

java 复制代码
/**
 * 查询模式
 */
public enum EsQueryMode {
    TERM,
    TERMS,
    WILDCARD,
    RANGE,
}
java 复制代码
/**
 * 通用转换
 *
 * @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;
    }

}
java 复制代码
/**
 * @description:       通用转换
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface GeneralConvert {

    /**
     * 获取键值
     *
     * @return 返回键值
     */
    String key();

    /**
     * 获取当前ES查询模式
     *
     * @return 返回当前ES查询模式
     */
    EsQueryMode mode();

}
java 复制代码
@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);
    }
}
相关推荐
转调6 分钟前
每日一练:地下城游戏
开发语言·c++·算法·leetcode
Java探秘者7 分钟前
Maven下载、安装与环境配置详解:从零开始搭建高效Java开发环境
java·开发语言·数据库·spring boot·spring cloud·maven·idea
2303_8120444616 分钟前
Bean,看到P188没看了与maven
java·开发语言
秋夫人19 分钟前
idea 同一个项目不同模块如何设置不同的jdk版本
java·开发语言·intellij-idea
不穿格子衬衫35 分钟前
常用排序算法(下)
c语言·开发语言·数据结构·算法·排序算法·八大排序
萧鼎41 分钟前
Python调试技巧:高效定位与修复问题
服务器·开发语言·python
wdxylb42 分钟前
使用C++的OpenSSL 库实现 AES 加密和解密文件
开发语言·c++·算法
Geek之路1 小时前
QT系统学习篇(1)
开发语言·qt·学习
罗曼蒂克在消亡1 小时前
GraphQL规范
开发语言·graphql
HealthScience1 小时前
怎么将bash(sh)的所有输出保存到log/txt中?
开发语言·bash