elasticsearch-java api 8 升级

本文详细描述了如何在项目从SpringBoot2升级到SpringBoot3时,从es7的elasticsearch-rest-high-level-client客户端迁移到es8的elasticsearch-java。文章重点介绍了依赖调整、查询API代码的调整以及聚合统计的更新,供开发者参考.
摘要由CSDN通过智能技术生成

es client api 升级

背景

公司项目从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>

查询api代码调整

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

创建client
@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
  
// 构建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);
    }
}

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个简单的ElasticSearch聚合的Java API示例: ```java import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCount; import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; public class ElasticSearchAggregationExample { public static void main(String[] args) { // 创建ElasticSearch客户端 Client client = // ...; // 构建查询条件 QueryBuilder query = QueryBuilders.boolQuery() .must(rangeQuery("timestamp").gte("2022-01-01T00:00:00").lte("2022-01-31T23:59:59")); // 构建聚合条件 AggregationBuilder aggregation = AggregationBuilders .dateHistogram("sales_over_time") .field("timestamp") .dateHistogramInterval(DateHistogramInterval.DAY) .subAggregation( AggregationBuilders .terms("product_types") .field("product_type") .subAggregation( AggregationBuilders.sum("total_sales").field("sales"), AggregationBuilders.count("transaction_count").field("transaction_id") ) ); // 执行查询 SearchResponse response = client.prepareSearch("my_index") .setQuery(query) .addAggregation(aggregation) .execute() .actionGet(); // 解析聚合结果 Histogram histogram = response.getAggregations().get("sales_over_time"); for (Histogram.Bucket bucket : histogram.getBuckets()) { System.out.println("Date: " + bucket.getKeyAsString()); Terms productTypes = bucket.getAggregations().get("product_types"); for (Terms.Bucket productType : productTypes.getBuckets()) { System.out.println("Product Type: " + productType.getKeyAsString()); Sum totalSales = productType.getAggregations().get("total_sales"); System.out.println("Total Sales: " + totalSales.getValue()); ValueCount transactionCount = productType.getAggregations().get("transaction_count"); System.out.println("Transaction Count: " + transactionCount.getValue()); } } // 关闭客户端 client.close(); } } ``` 这个示例通过ElasticSearchJava API执行了一个聚合,其中包含了两层嵌套聚合,分别按照日期和产品类型对销售数据进行了汇总,输出了每个日期和产品类型的销售总额和交易次数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值