← 返回首页

Java Elasticsearch操作详解

📂 java ⏱ 3 min 503 words

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注解和复杂条件查询。掌握聚合和高亮功能可以构建强大的搜索体验。