Elasticsearch Java API 深度实战:20个高频使用场景代码示例

477 阅读3分钟

Hello 大家好,我是兔子 本文将基于Java API Client 8.x版本,通过20个生产级代码示例,深入讲解Elasticsearch的日常高频操作。所有示例可直接复制到项目中运行,并附有最佳实践建议。

废话不多说,上干货


一、基础CRUD操作

1.1 创建索引(指定Mapping)
// 定义商品Mapping
Map<String, Property> properties = new HashMap<>();
properties.put("id", Property.of(p -> p.keyword(k -> k.ignoreAbove(256))));
properties.put("name", Property.of(p -> p.text(t -> t.fielddata(true))));
properties.put("price", Property.of(p -> p.double_(d -> d.index(true))));
properties.put("createTime", Property.of(p -> p.date(d -> d.format("epoch_millis"))));

CreateIndexRequest request = CreateIndexRequest.of(c -> c
    .index("products")
    .settings(s -> s
        .numberOfShards(3)
        .numberOfReplicas(1)
    )
    .mappings(m -> m
        .properties(properties)
    )
);

client.indices().create(request);
1.2 单文档写入(指定ID)
Product product = new Product("P1001", "无线蓝牙耳机", 299.00);

IndexRequest<Product> request = IndexRequest.of(i -> i
    .index("products")
    .id(product.getId())
    .document(product)
    .opType(OpType.Create) // 强制创建,存在则报错
);

IndexResponse response = client.index(request);
System.out.println("文档版本:" + response.version());
1.3 更新文档部分字段
Map<String, JsonData> updateFields = new HashMap<>();
updateFields.put("price", JsonData.of(259.00));

UpdateRequest<Product, Product> request = UpdateRequest.of(u -> u
    .index("products")
    .id("P1001")
    .doc(updateFields)
    .docAsUpsert(false) // 不存在不创建
    .retryOnConflict(3) // 冲突重试
);

UpdateResponse<Product> response = client.update(request, Product.class);
1.4 批量删除文档
BulkRequest request = BulkRequest.of(b -> b
    .index("products")
    .operations(
        new BulkOperation.Builder().delete(d -> d.id("P1001")).build(),
        new BulkOperation.Builder().delete(d -> d.id("P1002")).build()
    )
);

BulkResponse response = client.bulk(request);
if (response.errors()) {
    response.items().forEach(item -> {
        if (item.error() != null) {
            System.err.println("删除失败:" + item.id());
        }
    });
}

二、高级搜索功能

2.1 多字段高亮查询
SearchRequest request = SearchRequest.of(s -> s
    .index("products")
    .query(q -> q
        .multiMatch(m -> m
            .query("智能手表")
            .fields("name^3", "description")
        )
    )
    .highlight(h -> h
        .preTags("<em>")
        .postTags("</em>")
        .fields(
            "name", f -> f.numberOfFragments(0),
            "description", f -> f.fragmentSize(150)
        )
    )
);

SearchResponse<Product> response = client.search(request, Product.class);

response.hits().hits().forEach(hit -> {
    Map<String, List<String>> highlights = hit.highlight();
    System.out.println("高亮内容:" + highlights.get("name").get(0));
});
2.2 分页查询优化(search_after)
SearchRequest request = SearchRequest.of(s -> s
    .index("products")
    .size(100)
    .sort(so -> so.field(f -> f.field("price").order(SortOrder.Desc)))
    .sort(so -> so.field(f -> f.field("_id").order(SortOrder.Asc)))
    .searchAfter(JsonData.of("1599.00"), JsonData.of("P1003")) // 上一页最后结果值
);

SearchResponse<Product> response = client.search(request, Product.class);
2.3 模糊搜索(应对拼写错误)
SearchRequest request = SearchRequest.of(s -> s
    .index("products")
    .query(q -> q
        .fuzzy(f -> f
            .field("name")
            .value("手表")
            .fuzziness("1") // 允许1个字符差异
            .prefixLength(2) // 前2个字符必须精确
        )
    )
);

#### 三、聚合分析实战 ##### 3.1 价格区间分布统计
SearchRequest request = SearchRequest.of(s -> s
    .index("products")
    .size(0)
    .aggregations("price_ranges", a -> a
        .range(r -> r
            .field("price")
            .ranges(
                new Range.Builder().to(100.0).build(),
                new Range.Builder().from(100.0).to(500.0).build(),
                new Range.Builder().from(500.0).build()
            )
        )
    )
);

SearchResponse<Product> response = client.search(request, Product.class);

RangeAggregate ranges = response.aggregations().get("price_ranges").range();
ranges.buckets().array().forEach(bucket -> {
    System.out.printf("价格区间 %s 有 %d 件商品%n", 
        bucket.key(), bucket.docCount());
});
3.2 按品牌分组统计(Top10)
SearchRequest request = SearchRequest.of(s -> s
    .index("products")
    .aggregations("top_brands", a -> a
        .terms(t -> t
            .field("brand.keyword")
            .size(10)
            .shardSize(100) // 提升准确度
            .order(List.of(
                new TermSortOrder.Builder().key("_count").order(SortOrder.Desc).build()
            ))
        )
    )
);

#### 四、性能优化技巧 ##### 4.1 查询结果过滤(提升性能)
SearchRequest request = SearchRequest.of(s -> s
    .index("products")
    .query(q -> q
        .bool(b -> b
            .filter(f -> f // 不参与评分
                .term(t -> t.field("category").value("电子产品"))
            )
            .must(m -> m
                .match(mt -> mt.field("name").query("智能"))
            )
        )
    )
);
4.2 字段数据预加载
UpdateSettingsRequest request = UpdateSettingsRequest.of(u -> u
    .index("products")
    .settings(s -> s
        .indexing(i -> i
            .slowlog(sl -> sl
                .level("info")
                .source("1000") // 记录超过1秒的索引操作
            )
        )
        .mapping(m -> m
            .totalFields(tf -> tf.limit(1000)) // 限制字段数量
        )
    )
);

五、异常处理与监控

5.1 请求超时控制
SearchRequest request = SearchRequest.of(s -> s
    .index("products")
    .timeout("5s") // 查询超时时间
);

try {
    SearchResponse<Product> response = client.search(request, Product.class);
} catch (ElasticsearchException e) {
    if (e.status() == 408) {
        System.err.println("查询超时,建议优化查询条件");
    }
}
5.2 批量操作错误处理
BulkRequest request = BulkRequest.of(b -> b
    .operations(/* 批量操作 */)
);

BulkResponse response = client.bulk(request);

response.items().forEach(item -> {
    if (item.error() != null) {
        System.err.printf("操作失败:%s,原因:%s%n", 
            item.operationType(), item.error().reason());
    }
});

六、企业级实战场景

6.1 日志搜索系统(包含时间范围过滤)
SearchRequest request = SearchRequest.of(s -> s
    .index("app-logs-*")
    .query(q -> q
        .bool(b -> b
            .must(m -> m
                .matchPhrase(mp -> mp.field("message").query("ERROR"))
            )
            .filter(f -> f
                .range(r -> r
                    .field("@timestamp")
                    .gte(JsonData.of("now-1h/d"))
                    .lte(JsonData.of("now/d"))
                )
            )
        )
    )
);
6.2 电商商品推荐(结合用户画像)
SearchRequest request = SearchRequest.of(s -> s
    .index("products")
    .query(q -> q
        .functionScore(fs -> fs
            .query(q2 -> q2.matchAll(m -> m))
            .functions(fn -> fn
                .filter(f -> f.term(t -> t.field("tags").value(userFavoriteTag)))
                .weight(2.0)
            )
            .functions(fn -> fn
                .filter(f -> f.range(r -> r.field("sales").gte(1000)))
                .weight(1.5)
            )
            .scoreMode(FunctionScoreMode.Sum)
            .boostMode(FunctionBoostMode.Multiply)
        )
    )
);

最佳实践总结

1. 索引设计原则:
  • 控制分片大小(20-50GB为佳)
  • 禁用_all字段(7.x后默认禁用)
  • 对不需要分析的字段使用keyword类型
2. 查询性能优化:
  • 优先使用filter上下文
  • 避免wildcard前导通配符
  • 合理使用copy_to合并字段
3. Java客户端使用:
  • 复用Transport对象(线程安全)
  • 批量操作设置合理并发
  • 定期检查集群健康状态

欢迎各位在评论区讨论~

喜欢的话可以给博主点点关注哦 ➕