Java Elasticsearch操作详解
Java Elasticsearch操作详解
什么是Elasticsearch
Elasticsearch是一个分布式、RESTful风格的搜索和分析引擎,适合全文搜索、日志分析和实时数据分析。
Spring Data Elasticsearch
依赖配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
配置
spring:
elasticsearch:
uris: http://localhost:9200
文档定义
@Document(indexName = "products")
public class Product {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String description;
@Field(type = FieldType.Double)
private Double price;
@Field(type = FieldType.Keyword)
private String category;
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime createTime;
}
Repository查询
@Repository
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
// 方法名查询
List<Product> findByName(String name);
List<Product> findByNameContaining(String keyword);
List<Product> findByCategory(String category);
List<Product> findByPriceBetween(Double min, Double max);
List<Product> findByCategoryAndPriceLessThan(String category, Double price);
// @Query注解
@Query("{\"match\": {\"name\": \"?0\"}}")
List<Product> searchByName(String keyword);
@Query("{\"bool\": {\"must\": [{\"match\": {\"name\": \"?0\"}}, {\"range\": {\"price\": {\"gte\": ?1, \"lte\": ?2}}}]}}")
List<Product> searchByNameAndPrice(String keyword, Double minPrice, Double maxPrice);
}
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public Product createProduct(Product product) {
return productRepository.save(product);
}
public List<Product> searchProducts(String keyword) {
return productRepository.findByNameContaining(keyword);
}
public List<Product> searchByCategoryAndPrice(String category, Double maxPrice) {
return productRepository.findByCategoryAndPriceLessThan(category, maxPrice);
}
}
ElasticsearchOperations高级查询
@Component
public class ProductSearchRepository {
private final ElasticsearchOperations operations;
public ProductSearchRepository(ElasticsearchOperations operations) {
this.operations = operations;
}
// 多条件查询
public List<Product> search(String keyword, String category, Double minPrice, Double maxPrice) {
NativeQuery query = new NativeQueryBuilder()
.withQuery(q -> q.bool(b -> {
if (keyword != null) {
b.must(m -> m.match(mm -> mm.field("name").query(keyword)));
}
if (category != null) {
b.filter(f -> f.term(t -> t.field("category").value(category)));
}
if (minPrice != null || maxPrice != null) {
b.filter(f -> f.range(r -> {
r.field("price");
if (minPrice != null) r.gte(minPrice);
if (maxPrice != null) r.lte(maxPrice);
return r;
}));
}
return b;
}))
.withSort(Sort.by(Sort.Direction.DESC, "createTime"))
.withPageable(PageRequest.of(0, 20))
.build();
SearchHits<Product> hits = operations.search(query, Product.class);
return hits.getSearchHits().stream()
.map(SearchHit::getContent)
.toList();
}
// 高亮查询
public List<Product> searchWithHighlight(String keyword) {
NativeQuery query = new NativeQueryBuilder()
.withQuery(q -> q.match(m -> m.field("name").query(keyword)))
.withHighlightBuilder(new HighlightBuilder()
.field("name")
.preTags("<em>")
.postTags("</em>"))
.build();
SearchHits<Product> hits = operations.search(query, Product.class);
return hits.getSearchHits().stream()
.map(hit -> {
Product product = hit.getContent();
List<String> highlights = hit.getHighlightField("name");
if (!highlights.isEmpty()) {
product.setName(highlights.get(0));
}
return product;
})
.toList();
}
// 聚合查询
public Map<String, Long> getCategoryCounts() {
NativeQuery query = new NativeQueryBuilder()
.withAggregation("categories",
Aggregation.of(a -> a.terms(t -> t.field("category"))))
.build();
SearchHits<Product> hits = operations.search(query, Product.class);
Aggregations aggregations = hits.getAggregations();
// 解析聚合结果
return Map.of();
}
}
批量操作
@Service
public class ProductBatchService {
private final ElasticsearchOperations operations;
public void bulkIndex(List<Product> products) {
List<IndexQuery> queries = products.stream()
.map(product -> new IndexQueryBuilder()
.withId(product.getId())
.withObject(product)
.build())
.toList();
operations.bulkIndex(queries, Product.class);
}
public void bulkUpdate(List<Product> products) {
List<UpdateQuery> queries = products.stream()
.map(product -> new UpdateQueryBuilder()
.withId(product.getId())
.withUpdateRequest(new UpdateRequest()
.doc(product))
.build())
.toList();
operations.bulkUpdate(queries, Product.class);
}
public void bulkDelete(List<String> ids) {
List<DeleteQuery> queries = ids.stream()
.map(id -> new DeleteQueryBuilder()
.withId(id)
.build())
.toList();
operations.bulkDelete(queries, Product.class);
}
}
REST API使用
@Component
public class ElasticsearchRestClient {
private final RestClient restClient;
public ElasticsearchRestClient(RestClient restClient) {
this.restClient = restClient;
}
public String search(String index, String json) throws IOException {
Request request = new Request("POST", "/" + index + "/_search");
request.setJsonEntity(json);
Response response = restClient.performRequest(request);
return EntityUtils.toString(response.getEntity());
}
public String getIndexMapping(String index) throws IOException {
Request request = new Request("GET", "/" + index + "/_mapping");
Response response = restClient.performRequest(request);
return EntityUtils.toString(response.getEntity());
}
}
总结
Elasticsearch是全文搜索和日志分析的首选方案。Spring Data Elasticsearch提供了简洁的查询API,支持方法名查询、@Query注解和复杂条件查询。掌握聚合和高亮功能可以构建强大的搜索体验。