(转载)es 常用聚合函数与builder api间转换关系

本文分享了如何在Elasticsearch中使用Shadow Pseudo技术进行聚合操作,深入探讨了其在搜索和数据分析中的应用实例,适合高级用户和开发者阅读。
<think> 我们参考了用户提供的引用内容,其中引用[1]和引用[4]都涉及到了filter查询(即过滤查询)的示例。但是请注意,用户使用的是旧版本的TransportClient(在Elasticsearch 7.x之后已被弃用,8.x已移除)。而用户当前的问题是关于Elasticsearch Java API Client(8.x+版本)的,因此我们需要使用新的Java API Client来实现filter查询。 在新版的Elasticsearch Java API Client中,查询和过滤已经合并,因为过滤(filter)在ES中已经作为布尔查询(bool query)的一部分,并且可以利用查询缓存。我们通常使用布尔查询的filter子句来实现过滤。 下面我们将使用Elasticsearch Java API Client(8.x)来实现一个filter查询,类似于引用[1]中的例子:在索引中过滤出名为"zhangsan"的文档。 ### 步骤: 1. 构建一个布尔查询,其中filter子句使用term查询来精确匹配"name"字段为"zhangsan"的文档。 2. 执行搜索请求。 3. 处理搜索结果。 ### 示例代码: ```java import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch.core.SearchRequest; import co.elastic.clients.elasticsearch.core.SearchResponse; import co.elastic.clients.elasticsearch.core.search.Hit; import co.elastic.clients.elasticsearch.core.search.HitsMetadata; import co.elastic.clients.elasticsearch.core.search.TotalHits; import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation; import co.elastic.clients.json.JsonData; import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.rest_client.RestClientTransport; import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class FilterQueryExample { public static void main(String[] args) throws IOException { // 创建ES客户端 RestClient restClient = RestClient.builder( new HttpHost("localhost", 9200) ).build(); ElasticsearchTransport transport = new RestClientTransport( restClient, new JacksonJsonpMapper() ); ElasticsearchClient client = new ElasticsearchClient(transport); // 执行filter查询 List<String> results = queryByFilter(client, "your_index_name"); // 输出结果 for (String result : results) { System.out.println(result); } // 关闭客户端 restClient.close(); } /** * 使用filter方式查询数据(新版API) * @param client Elasticsearch客户端 * @param index 索引名 * @return 匹配文档的source字符串列表 */ public static List<String> queryByFilter(ElasticsearchClient client, String index) throws IOException { // 构建查询:布尔查询的filter子句 SearchResponse<Object> response = client.search(s -> s .index(index) .query(q -> q .bool(b -> b .filter(f -> f .term(t -> t .field("name") .value("zhangsan") ) ) ) ), Object.class // 这里使用Object.class,因为我们直接获取_source字符串 ); List<String> docList = new ArrayList<>(); HitsMetadata<Object> hits = response.hits(); TotalHits total = hits.total(); if (total != null && total.value() > 0) { for (Hit<Object> hit : hits.hits()) { // 将_source转换为字符串(因为我们不知道具体的类型,这里用JsonData) if (hit.source() != null) { // 由于我们使用Object.class,hit.source()返回的是Map,我们可以将其转为JSON字符串 // 或者,如果我们想直接获取_source字符串,可以使用低级客户端的方式,但这里高级客户端没有直接提供。 // 替代方案:我们可以使用带有JsonpMapper的解析器来将source转换为字符串,但这样比较麻烦。 // 另一种方法:我们可以在搜索请求中指定返回_source作为字符串?但是高级客户端没有这个选项。 // 因此,我们可以改变方法:使用一个通用的Map来接收,然后序列化为字符串,或者使用具体的实体类。 // 这里我们采用折中方式:如果我们的实体类未知,我们可以使用JsonData来包装 // 但我们在请求时使用了Object.class,所以这里hit.source()是Map,我们可以用JSON库转成字符串 // 这里为了简单,使用Jackson将Map转换为字符串(需要引入jackson-databind) // 但是,为了减少依赖,我们也可以直接输出Map,或者使用JsonData的toString()方法(但格式可能不是标准的JSON字符串) // 注意:如果使用JsonData,可以这样: // JsonData source = hit.source(); // 但是我们在查询时指定了Object.class,所以这里我们不能直接得到JsonData。 // 因此,我们调整一下:使用JsonData作为源的类型 // 重新构建查询请求,将结果类型指定为JsonData } } } // 由于上面获取source字符串的方式需要调整,我们重新写一个方法,使用JsonData作为文档类型 // 下面我们重新实现一个更合适的方法 return docList; } // 改进方法:使用JsonData来接收_source public static List<String> queryByFilterImproved(ElasticsearchClient client, String index) throws IOException { SearchResponse<JsonData> response = client.search(s -> s .index(index) .query(q -> q .bool(b -> b .filter(f -> f .term(t -> t .field("name") .value("zhangsan") ) ) ) ), JsonData.class // 使用JsonData作为_source类型 ); List<String> docList = new ArrayList<>(); for (Hit<JsonData> hit : response.hits().hits()) { // 使用JsonData的toJson()方法可以获取到Json字符串 docList.add(hit.source().toJson()); } return docList; } } ``` ### 说明: 1. **过滤查询**:我们使用布尔查询的`filter`子句,其中包含一个`term`查询,用于精确匹配"name"字段为"zhangsan"的文档。 2. **结果处理**:我们使用`JsonData`作为文档类型,这样可以直接通过`toJson()`方法将文档内容转为JSON字符串。这样处理起来比较方便,不需要预先定义实体类。 3. **注意**:新版的Java API Client是强类型化的,因此我们需要指定搜索结果的类型。如果不想定义具体的实体类,使用`JsonData`是一个灵活的选择。 ### 旧版TransportClient的区别: - 新版API使用链式调用和函数式编程风格。 - 不再需要区分query和filter,而是统一在布尔查询中使用`must`(必须匹配,影响评分)、`should`(或条件)、`filter`(过滤,不评分)等。 - 过滤条件通常放在布尔查询的`filter`子句中,这样可以利用ES的查询缓存,提高性能。 ### 性能提示: - 过滤查询(filter)不参评分,因此效率更高,并且结果可以被缓存。 - 在组合查询中,将不参评分的条件(如范围过滤、精确匹配)放在`filter`子句中,而全文检索等放在`must`或`should`中[^1][^4]。 §§相关问题§§ 1. Elasticsearch Java API Client 中如何组合多个过滤条件? 2. 布尔查询中`must`、`should`和`filter`有什么区别? 3. 如何在新版API中实现分页和排序? 4. 如何通过Java API Client实现聚合(aggregations)查询? [^1]: 过滤器(Filter)在布尔查询中的应用示例 [^4]: 多条件组合查询的实现方式 </think> # Elasticsearch Java API Client 实现 Filter 查询详解 在 Elasticsearch Java API Client (8.x+) 中,filter 查询是性能优化的重要手段,它不参评分但能高效过滤文档。以下是完整的实现指南: ## 一、基本 Filter 查询实现 ```java import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch.core.SearchRequest; import co.elastic.clients.elasticsearch.core.SearchResponse; import co.elastic.clients.elasticsearch.core.search.Hit; public List<String> filterQuery(ElasticsearchClient client, String index) throws IOException { // 构建 term filter 精确匹配 SearchResponse<Object> response = client.search(s -> s .index(index) .query(q -> q .bool(b -> b .filter(f -> f .term(t -> t // term 过滤器 .field("name") .value("zhangsan") ) ) ) ), Object.class // 使用 Object 类型接收原始 JSON ); List<String> results = new ArrayList<>(); for (Hit<Object> hit : response.hits().hits()) { // 获取文档原始 JSON 字符串 results.add(hit.source().toString()); } return results; } ``` ## 二、组合多条件 Filter 查询 ```java // 组合多个过滤条件 SearchResponse<Object> response = client.search(s -> s .index(index) .query(q -> q .bool(b -> b .filter( f -> f.term(t -> t.field("status").value("active")), // 条件1 f -> f.range(r -> r.field("price").gte(JsonData.of(100))) // 条件2 ) .must(m -> m.match(ma -> ma.field("description").query("高性能"))) // 评分查询 ) ), Object.class ); ``` ## 三、Filter 查询最佳实践 ### 1. 性能优化技巧 - **Filter 上下文**:将不参评分的条件放入 `filter` 子句 - **缓存利用**:ES 会自动缓存常用 filter 结果 - **组合顺序**:将最具限制性的 filter 放在前面 ```java // 优化后的组合查询 bool(b -> b .filter(f -> f.term(t -> t.field("category").value("electronics"))) // 高选择性条件 .filter(f -> f.range(r -> r.field("stock").gt(JsonData.of(0)))) // 中等选择性 .must(m -> m.match(ma -> ma.field("name").query("手机"))) // 评分查询 ) ``` ### 2. 特殊 Filter 类型 ```java // 范围过滤器 filter(f -> f.range(r -> r .field("create_time") .gte(JsonData.of("2023-01-01")) .lte(JsonData.of("2023-12-31")) )) // 存在性检查 filter(f -> f.exists(e -> e.field("tags"))) // 多值匹配 filter(f -> f.terms(t -> t .field("color") .terms(v -> v.value(List.of("red", "blue"))) )) ``` ## 四、传统 TransportClient 对比 | 特性 | 旧版 TransportClient | 新版 Java API Client | |------|----------------------|---------------------| | 语法风格 | Builder 模式 | 函数式 DSL | | 类型安全 | 弱类型 | 强类型检查 | | 版本兼容 | ES 7.x 及之前 | ES 8.x+ | | 连接管理 | 需手动管理 | 自动连接池 | | 异步支持 | 有限 | 完善异步API | > **迁移提示**:新版 API 将 filter 整合到 bool 查询中,不再有单独的 filter 方法[^1][^4]。 ## 五、错误处理调试 ```java try { SearchResponse<Object> response = client.search(...); // 检查是否有分片失败 if (response.shards().failed() > 0) { for (ShardFailure failure : response.shards().failures()) { System.err.println("分片失败: " + failure.reason()); } } } catch (ElasticsearchException e) { // 处理ES特定异常 System.err.println("ES错误: " + e.getMessage()); } catch (IOException e) { // 处理IO异常 System.err.println("网络错误: " + e.getMessage()); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值