Java操作Elasticsearch案例

wen java案例 6

Java操作Elasticsearch实战指南:从增删改查到聚合分析(附完整代码案例)


目录导读

  1. 为什么Java开发者需要掌握Elasticsearch?
  2. 环境准备:Maven依赖与连接客户端初始化
  3. 核心操作实战:索引管理(创建/删除)
  4. 文档CRUD:增、删、改、查(含批量操作)
  5. 高级查询:BoolQuery组合条件与分页排序
  6. 聚合分析:按字段分组统计(Group By)
  7. 高频问答(FAQ):连接超时、版本兼容、性能优化
  8. 最佳实践与避坑指南

为什么Java开发者需要掌握Elasticsearch?

在当今微服务架构中,Elasticsearch(简称ES)已成为日志检索、商品搜索、数据分析的标配,Java作为后端主力语言,通过官方High Level REST Client(7.x)或Java API Client(8.x+)操作ES是核心技能。实际面试中,80%的候选人会写“熟悉ES”,但只有30%能完整写出CRUD代码——本案例将直接解决这一痛点。

Java操作Elasticsearch案例


环境准备:Maven依赖与客户端初始化

依赖引入(以Spring Boot 2.7 + ES 7.17为例):

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.17.9</version>
</dependency>

客户端初始化(高可用连接池配置):

@Configuration
public class EsConfig {
    @Bean
    public RestHighLevelClient client() {
        return new RestHighLevelClient(RestClient.builder(
            new HttpHost("192.168.1.100", 9200, "http")
        ).setRequestConfigCallback(builder -> 
            builder.setConnectTimeout(5000).setSocketTimeout(60000)
        ));
    }
}

⚠️ 注意:若使用ES 8.x,需改用elasticsearch-java(新API),包路径完全不同。


核心操作实战:索引管理(创建/删除)

创建索引并指定分词器

CreateIndexRequest request = new CreateIndexRequest("products");
request.settings(Settings.builder()
    .put("index.number_of_shards", 3)
    .put("index.number_of_replicas", 2));
request.mapping("{\"properties\":{\"title\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\"}}}", XContentType.JSON);
client.indices().create(request, RequestOptions.DEFAULT);

删除索引(谨慎操作):

DeleteIndexRequest request = new DeleteIndexRequest("products");
AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);

文档CRUD:增、删、改、查(含批量操作)

新增文档(自动生成ID):

Map<String, Object> doc = new HashMap<>();
doc.put("title", "华为Mate60 Pro");
doc.put("price", 6999.00);
IndexRequest request = new IndexRequest("products").source(doc);
IndexResponse response = client.index(request, RequestOptions.DEFAULT);

根据ID查询

GetRequest request = new GetRequest("products", "123");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
Map<String, Object> source = response.getSourceAsMap(); // 转Map

批量插入(性能提升10倍)

BulkRequest bulk = new BulkRequest();
for (int i = 0; i < 1000; i++) {
    bulk.add(new IndexRequest("products")
        .id(String.valueOf(i))
        .source(jsonString, XContentType.JSON));
}
BulkResponse response = client.bulk(bulk, RequestOptions.DEFAULT);

高级查询:BoolQuery组合条件与分页排序

经典场景包含“手机”且价格在3000-8000之间,按价格降序:

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
    .must(QueryBuilders.matchQuery("title", "手机"))
    .filter(QueryBuilders.rangeQuery("price").gte(3000).lte(8000));
sourceBuilder.query(boolQuery);
sourceBuilder.from(0).size(20);
sourceBuilder.sort("price", SortOrder.DESC);
SearchRequest request = new SearchRequest("products").source(sourceBuilder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
for (SearchHit hit : response.getHits()) {
    System.out.println(hit.getSourceAsString());
}

聚合分析:按字段分组统计(Group By)

统计每个品牌的平均价格(类似SQL的GROUP BY brand):

AggregationBuilder agg = AggregationBuilders.terms("group_by_brand")
    .field("brand.keyword")  // 注意keyword字段
    .subAggregation(AggregationBuilders.avg("avg_price").field("price"));
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
    .aggregation(agg).size(0);  // size=0不返回原始数据
SearchResponse response = client.search(new SearchRequest("products").source(sourceBuilder), RequestOptions.DEFAULT);
Terms terms = response.getAggregations().get("group_by_brand");
for (Terms.Bucket bucket : terms.getBuckets()) {
    System.out.println("品牌=" + bucket.getKeyAsString() + 
        ", 平均价格=" + bucket.getAggregations().get("avg_price").toString());
}

高频问答(FAQ)

Q1:连接ES超时如何处理?

  • 检查HttpHost端口是否为9300(错误)或9200(正确)。
  • 增大setSocketTimeout至60000ms,并确认防火墙放行。

Q2:es版本与Java客户端不兼容怎么办?

  • 严格匹配大版本(7.x配7.x客户端),若使用Spring Data Elasticsearch,需与Spring Boot版本对应。

Q3:批量插入太慢?

  • 开启Bulk的setBulkActions(5000)setBulkSize(new ByteSizeValue(10, ByteSizeUnit.MB))
  • 禁用refreshrequest.setRefreshPolicy(WriteRequest.RefreshPolicy.NONE))。

Q4:查询结果出现null字段?

  • 确认字段类型是否为textkeyword混用,聚合排序必须用.keyword后缀。

最佳实践与避坑指南

  • 连接复用:使用单例RestHighLevelClient,避免频繁创建。
  • 索引映射:提前定义,避免动态映射导致类型错误。
  • 分页限制:深度分页(超过10000条)改用search_after
  • 异步优化:尝试client.searchAsync()提升并发能力。
  • 日志监控:开启SlowLog定位慢查询,通过_cat/indices?v查看分片占用。

希望这个案例能成为你Java操作ES的“字典式”文章。纸上得来终觉浅,绝知此事要躬行——赶紧复制代码到你的IDE中跑一遍,遇到任何异常欢迎在评论区留言,我会第一时间回复!

抱歉,评论功能暂时关闭!