Elasticsearch 与 Java 集成复杂示例

实现一个基于电商场景的复杂示例,包含商品搜索、过滤、聚合分析、地理位置查询等功能。

一、创建 Spring Boot 项目

使用 Spring Initializr 创建项目,添加以下依赖:

  • Spring Web
  • Spring Data Elasticsearch
  • Lombok (可选)
  • Spring Validation

或者手动添加依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

二、配置 Elasticsearch 连接

application.properties中添加:

spring.elasticsearch.uris=http://localhost:9200
spring.elasticsearch.username=elastic
spring.elasticsearch.password=your_password

三、定义文档模型

创建商品文档类:

package com.example.esdemo.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.*;

import java.util.Date;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
@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.Integer)
    private int stock;  // 库存

    @Field(type = FieldType.Date)
    private Date createTime;  // 创建时间

    @Field(type = FieldType.Keyword)
    private String category;  // 分类

    @Field(type = FieldType.Nested)
    private List<Attribute> attributes;  // 商品属性

    @Field(type = FieldType.GeoPoint)
    private GeoPoint location;  // 地理位置

    @Field(type = FieldType.Boolean)
    private boolean onSale;  // 是否促销

    @Field(type = FieldType.Integer)
    private int sales;  // 销量

    @Field(type = FieldType.Double)
    private double score;  // 评分
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Attribute {
    @Field(type = FieldType.Keyword)
    private String name;  // 属性名
    
    @Field(type = FieldType.Keyword)
    private String value;  // 属性值
}

四、创建自定义 Repository 接口

package com.example.esdemo.repository;

import com.example.esdemo.model.Product;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import java.util.Date;
import java.util.List;

public interface ProductRepository extends ElasticsearchRepository<Product, String>, CustomProductRepository {

    // 基本查询方法
    List<Product> findByName(String name);
    
    Page<Product> findByCategory(String category, Pageable pageable);
    
    // 自定义查询(使用JSON格式)
    @Query("{\"range\": {\"price\": {\"gte\": ?0, \"lte\": ?1}}}")
    List<Product> findByPriceRange(double minPrice, double maxPrice);
    
    // 复杂查询示例
    List<Product> findByOnSaleAndCreateTimeAfter(boolean onSale, Date date);
}

五、实现自定义查询接口

package com.example.esdemo.repository;

import com.example.esdemo.model.Product;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

public interface CustomProductRepository {

    // 全文搜索 + 过滤
    Page<Product> search(String keyword, 
                         Double minPrice, 
                         Double maxPrice, 
                         String category,
                         List<String> attributes,
                         Boolean onSale,
                         Pageable pageable);
    
    // 地理位置搜索
    List<Product> searchByLocation(double lat, double lon, double distance, String unit);
    
    // 聚合分析:统计分类下的商品数量
    List<CategoryCount> countByCategory();
    
    // 聚合分析:价格区间分布
    List<PriceRange> analyzePriceDistribution();
    
    // 聚合分析:热门属性值
    List<AttributeValueCount> findPopularAttributes(int size);
}

// 聚合结果模型
interface CategoryCount {
    String getCategory();
    long getCount();
}

interface PriceRange {
    String getKey();
    long getCount();
}

interface AttributeValueCount {
    String getAttributeName();
    String getAttributeValue();
    long getCount();
}

六、实现自定义查询

package com.example.esdemo.repository.impl;

import com.example.esdemo.model.Product;
import com.example.esdemo.repository.CustomProductRepository;
import com.example.esdemo.repository.CategoryCount;
import com.example.esdemo.repository.PriceRange;
import com.example.esdemo.repository.AttributeValueCount;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.filter.Filter;
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedStats;
import org.elasticsearch.search.aggregations.metrics.StatsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository
public class CustomProductRepositoryImpl implements CustomProductRepository {

    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private ElasticsearchOperations elasticsearchOperations;

    @Override
    public Page<Product> search(String keyword, Double minPrice, Double maxPrice, String category,
                               List<String> attributes, Boolean onSale, Pageable pageable) {
        // 构建布尔查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        
        // 关键词搜索
        if (keyword != null && !keyword.isEmpty()) {
            MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(
                keyword, "name^3", "description^2", "attributes.value")
                .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
                .boost(1.0f);
            boolQuery.must(multiMatchQuery);
        }
        
        // 价格过滤
        if (minPrice != null || maxPrice != null) {
            RangeQueryBuilder priceQuery = QueryBuilders.rangeQuery("price");
            if (minPrice != null) priceQuery.gte(minPrice);
            if (maxPrice != null) priceQuery.lte(maxPrice);
            boolQuery.filter(priceQuery);
        }
        
        // 分类过滤
        if (category != null && !category.isEmpty()) {
            boolQuery.filter(QueryBuilders.termQuery("category.keyword", category));
        }
        
        // 属性过滤
        if (attributes != null && !attributes.isEmpty()) {
            BoolQueryBuilder attrBoolQuery = QueryBuilders.boolQuery();
            for (String attr : attributes) {
                String[] parts = attr.split(":");
                if (parts.length == 2) {
                    NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery(
                        "attributes",
                        QueryBuilders.boolQuery()
                            .must(QueryBuilders.termQuery("attributes.name.keyword", parts[0]))
                            .must(QueryBuilders.termQuery("attributes.value.keyword", parts[1])),
                        ScoreMode.Avg
                    );
                    attrBoolQuery.must(nestedQuery);
                }
            }
            boolQuery.filter(attrBoolQuery);
        }
        
        // 促销过滤
        if (onSale != null) {
            boolQuery.filter(QueryBuilders.termQuery("onSale", onSale));
        }
        
        // 构建搜索查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withQuery(boolQuery)
            .withPageable(pageable)
            .withSorts(
                SortBuilders.scoreSort().order(SortOrder.DESC),  // 按相关性排序
                SortBuilders.fieldSort("sales").order(SortOrder.DESC)  // 按销量排序
            )
            .build();
        
        // 执行搜索
        SearchHits<Product> searchHits = elasticsearchOperations.search(searchQuery, Product.class);
        List<Product> products = searchHits.map(SearchHit::getContent).toList();
        
        return new PageImpl<>(products, pageable, searchHits.getTotalHits());
    }

    @Override
    public List<Product> searchByLocation(double lat, double lon, double distance, String unit) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        
        // 构建地理位置查询
        GeoDistanceQueryBuilder geoQuery = QueryBuilders.geoDistanceQuery("location")
            .point(lat, lon)
            .distance(distance, unit);
        
        sourceBuilder.query(geoQuery);
        sourceBuilder.sort(SortBuilders.geoDistanceSort("location", lat, lon)
            .order(SortOrder.ASC)
            .unit(unit));
        
        SearchRequest searchRequest = new SearchRequest("products");
        searchRequest.source(sourceBuilder);
        
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            
            List<Product> products = new ArrayList<>();
            for (SearchHit hit : hits) {
                Product product = elasticsearchOperations.getElasticsearchConverter()
                    .read(Product.class, hit.getSourceAsMap());
                products.add(product);
            }
            
            return products;
        } catch (IOException e) {
            throw new RuntimeException("地理位置搜索失败", e);
        }
    }

    @Override
    public List<CategoryCount> countByCategory() {
        TermsAggregationBuilder aggregation = AggregationBuilders
            .terms("categories")
            .field("category.keyword")
            .size(10);
        
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.aggregation(aggregation);
        sourceBuilder.size(0);  // 不返回文档,只返回聚合结果
        
        SearchRequest searchRequest = new SearchRequest("products");
        searchRequest.source(sourceBuilder);
        
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            ParsedTerms categories = response.getAggregations().get("categories");
            
            List<CategoryCount> result = new ArrayList<>();
            for (Terms.Bucket bucket : categories.getBuckets()) {
                final String category = bucket.getKeyAsString();
                final long count = bucket.getDocCount();
                
                result.add(new CategoryCount() {
                    @Override
                    public String getCategory() {
                        return category;
                    }
                    
                    @Override
                    public long getCount() {
                        return count;
                    }
                });
            }
            
            return result;
        } catch (IOException e) {
            throw new RuntimeException("分类统计失败", e);
        }
    }

    @Override
    public List<PriceRange> analyzePriceDistribution() {
        // 定义价格区间
        Map<String, Range> priceRanges = new HashMap<>();
        priceRanges.put("0-500", new Range(0, 500));
        priceRanges.put("500-1000", new Range(500, 1000));
        priceRanges.put("1000-2000", new Range(1000, 2000));
        priceRanges.put("2000+", new Range(2000, null));
        
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        
        // 构建聚合查询
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(boolQuery);
        
        for (Map.Entry<String, Range> entry : priceRanges.entrySet()) {
            String key = entry.getKey();
            Range range = entry.getValue();
            
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            if (range.getFrom() != null) rangeQuery.gte(range.getFrom());
            if (range.getTo() != null) rangeQuery.lt(range.getTo());
            
            FilterAggregationBuilder filterAgg = AggregationBuilders
                .filter(key, rangeQuery);
            
            sourceBuilder.aggregation(filterAgg);
        }
        
        sourceBuilder.size(0);  // 不返回文档
        
        SearchRequest searchRequest = new SearchRequest("products");
        searchRequest.source(sourceBuilder);
        
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            
            List<PriceRange> result = new ArrayList<>();
            for (Map.Entry<String, Range> entry : priceRanges.entrySet()) {
                String key = entry.getKey();
                Filter filter = response.getAggregations().get(key);
                
                final String rangeKey = key;
                final long count = filter.getDocCount();
                
                result.add(new PriceRange() {
                    @Override
                    public String getKey() {
                        return rangeKey;
                    }
                    
                    @Override
                    public long getCount() {
                        return count;
                    }
                });
            }
            
            return result;
        } catch (IOException e) {
            throw new RuntimeException("价格分布分析失败", e);
        }
    }

    @Override
    public List<AttributeValueCount> findPopularAttributes(int size) {
        NestedAggregationBuilder nestedAgg = AggregationBuilders
            .nested("attributes_agg", "attributes");
        
        TermsAggregationBuilder attrNameAgg = AggregationBuilders
            .terms("attr_names")
            .field("attributes.name.keyword")
            .size(10);
        
        TermsAggregationBuilder attrValueAgg = AggregationBuilders
            .terms("attr_values")
            .field("attributes.value.keyword")
            .size(size);
        
        attrNameAgg.subAggregation(attrValueAgg);
        nestedAgg.subAggregation(attrNameAgg);
        
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.aggregation(nestedAgg);
        sourceBuilder.size(0);
        
        SearchRequest searchRequest = new SearchRequest("products");
        searchRequest.source(sourceBuilder);
        
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            Nested attributesAgg = response.getAggregations().get("attributes_agg");
            ParsedTerms attrNames = attributesAgg.getAggregations().get("attr_names");
            
            List<AttributeValueCount> result = new ArrayList<>();
            for (Terms.Bucket nameBucket : attrNames.getBuckets()) {
                String attrName = nameBucket.getKeyAsString();
                ParsedTerms attrValues = nameBucket.getAggregations().get("attr_values");
                
                for (Terms.Bucket valueBucket : attrValues.getBuckets()) {
                    final String finalAttrName = attrName;
                    final String attrValue = valueBucket.getKeyAsString();
                    final long count = valueBucket.getDocCount();
                    
                    result.add(new AttributeValueCount() {
                        @Override
                        public String getAttributeName() {
                            return finalAttrName;
                        }
                        
                        @Override
                        public String getAttributeValue() {
                            return attrValue;
                        }
                        
                        @Override
                        public long getCount() {
                            return count;
                        }
                    });
                }
            }
            
            return result;
        } catch (IOException e) {
            throw new RuntimeException("热门属性分析失败", e);
        }
    }
    
    // 内部类:表示价格区间
    private static class Range {
        private final Double from;
        private final Double to;
        
        public Range(Double from, Double to) {
            this.from = from;
            this.to = to;
        }
        
        public Double getFrom() {
            return from;
        }
        
        public Double getTo() {
            return to;
        }
    }
}

七、创建 Service 层

package com.example.esdemo.service;

import com.example.esdemo.model.Product;
import com.example.esdemo.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    /**
     * 保存商品
     */
    public Product save(Product product) {
        if (product.getCreateTime() == null) {
            product.setCreateTime(new Date());
        }
        return productRepository.save(product);
    }

    /**
     * 根据ID获取商品
     */
    public Product getById(String id) {
        return productRepository.findById(id).orElse(null);
    }

    /**
     * 获取所有商品
     */
    public Page<Product> getAll(Pageable pageable) {
        return productRepository.findAll(pageable);
    }

    /**
     * 复杂搜索
     */
    public Page<Product> search(String keyword, 
                               Double minPrice, 
                               Double maxPrice, 
                               String category,
                               List<String> attributes,
                               Boolean onSale,
                               Pageable pageable) {
        return productRepository.search(keyword, minPrice, maxPrice, category, attributes, onSale, pageable);
    }

    /**
     * 地理位置搜索
     */
    public List<Product> searchByLocation(double lat, double lon, double distance, String unit) {
        return productRepository.searchByLocation(lat, lon, distance, unit);
    }

    /**
     * 统计分类下的商品数量
     */
    public List<CategoryCount> countByCategory() {
        return productRepository.countByCategory();
    }

    /**
     * 分析价格分布
     */
    public List<PriceRange> analyzePriceDistribution() {
        return productRepository.analyzePriceDistribution();
    }

    /**
     * 查找热门属性
     */
    public List<AttributeValueCount> findPopularAttributes(int size) {
        return productRepository.findPopularAttributes(size);
    }

    // 聚合结果接口
    public interface CategoryCount {
        String getCategory();
        long getCount();
    }

    public interface PriceRange {
        String getKey();
        long getCount();
    }

    public interface AttributeValueCount {
        String getAttributeName();
        String getAttributeValue();
        long getCount();
    }
}

八、创建 Controller 层

package com.example.esdemo.controller;

import com.example.esdemo.model.Product;
import com.example.esdemo.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    /**
     * 添加商品
     */
    @PostMapping
    public Product addProduct(@RequestBody @Valid Product product) {
        return productService.save(product);
    }

    /**
     * 根据ID获取商品
     */
    @GetMapping("/{id}")
    public Product getProduct(@PathVariable String id) {
        return productService.getById(id);
    }

    /**
     * 获取所有商品
     */
    @GetMapping
    public Page<Product> getAllProducts(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size
    ) {
        return productService.getAll(PageRequest.of(page, size));
    }

    /**
     * 复杂搜索
     */
    @GetMapping("/search")
    public Page<Product> search(
        @RequestParam(required = false) String keyword,
        @RequestParam(required = false) Double minPrice,
        @RequestParam(required = false) Double maxPrice,
        @RequestParam(required = false) String category,
        @RequestParam(required = false) List<String> attributes,
        @RequestParam(required = false) Boolean onSale,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size
    ) {
        return productService.search(keyword, minPrice, maxPrice, category, attributes, onSale,
            PageRequest.of(page, size));
    }

    /**
     * 地理位置搜索
     */
    @GetMapping("/search/nearby")
    public List<Product> searchByLocation(
        @RequestParam double lat,
        @RequestParam double lon,
        @RequestParam(defaultValue = "10") double distance,
        @RequestParam(defaultValue = "km") String unit
    ) {
        return productService.searchByLocation(lat, lon, distance, unit);
    }

    /**
     * 统计分类下的商品数量
     */
    @GetMapping("/stats/categories")
    public List<ProductService.CategoryCount> countByCategory() {
        return productService.countByCategory();
    }

    /**
     * 分析价格分布
     */
    @GetMapping("/stats/prices")
    public List<ProductService.PriceRange> analyzePriceDistribution() {
        return productService.analyzePriceDistribution();
    }

    /**
     * 查找热门属性
     */
    @GetMapping("/stats/attributes")
    public List<ProductService.AttributeValueCount> findPopularAttributes(
        @RequestParam(defaultValue = "5") int size
    ) {
        return productService.findPopularAttributes(size);
    }
}

九、测试数据与验证

1. 添加测试数据
package com.example.esdemo;

import com.example.esdemo.model.Attribute;
import com.example.esdemo.model.GeoPoint;
import com.example.esdemo.model.Product;
import com.example.esdemo.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

@SpringBootApplication
public class EsDemoApplication implements CommandLineRunner {

    @Autowired
    private ProductService productService;

    public static void main(String[] args) {
        SpringApplication.run(EsDemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        // 添加测试数据
        List<Product> products = Arrays.asList(
            new Product(null, "苹果 iPhone 15", "全新一代智能手机", 7999.0, 100, new Date(), 
                "手机", 
                Arrays.asList(
                    new Attribute("品牌", "苹果"),
                    new Attribute("颜色", "黑色"),
                    new Attribute("存储容量", "256GB")
                ),
                new GeoPoint(39.9042, 116.4074),  // 北京坐标
                true, 500, 4.8),
            
            new Product(null, "华为 Mate 60 Pro", "高端旗舰手机", 6999.0, 80, new Date(), 
                "手机", 
                Arrays.asList(
                    new Attribute("品牌", "华为"),
                    new Attribute("颜色", "银色"),
                    new Attribute("存储容量", "512GB")
                ),
                new GeoPoint(30.2741, 120.1551),  // 杭州坐标
                true, 350, 4.9),
            
            new Product(null, "小米 14", "性能旗舰手机", 4999.0, 120, new Date(), 
                "手机", 
                Arrays.asList(
                    new Attribute("品牌", "小米"),
                    new Attribute("颜色", "蓝色"),
                    new Attribute("存储容量", "256GB")
                ),
                new GeoPoint(31.2304, 121.4737),  // 上海坐标
                false, 280, 4.7),
            
            new Product(null, "Apple MacBook Pro", "专业笔记本电脑", 14999.0, 50, new Date(), 
                "电脑", 
                Arrays.asList(
                    new Attribute("品牌", "苹果"),
                    new Attribute("屏幕尺寸", "14英寸"),
                    new Attribute("内存", "16GB")
                ),
                new GeoPoint(39.9042, 116.4074),
                true, 180, 4.9),
            
            new Product(null, "联想 ThinkPad X1 Carbon", "商务笔记本电脑", 12999.0, 60, new Date(), 
                "电脑", 
                Arrays.asList(
                    new Attribute("品牌", "联想"),
                    new Attribute("屏幕尺寸", "14英寸"),
                    new Attribute("内存", "32GB")
                ),
                new GeoPoint(39.9042, 116.4074),
                false, 120, 4.8)
        );

        // 保存到Elasticsearch
        products.forEach(productService::save);
        System.out.println("测试数据已添加");
    }
}
2. 测试 API
  1. 全文搜索

    bash

    GET http://localhost:8080/api/products/search?keyword=苹果&category=手机
    
  2. 价格过滤搜索

    bash

    GET http://localhost:8080/api/products/search?minPrice=5000&maxPrice=10000
    
  3. 属性过滤搜索

    bash

    GET http://localhost:8080/api/products/search?attributes=品牌:华为&attributes=存储容量:512GB
    
  4. 地理位置搜索

    bash

    GET http://localhost:8080/api/products/search/nearby?lat=39.9042&lon=116.4074&distance=1000
    
  5. 聚合分析

    bash

    GET http://localhost:8080/api/products/stats/categories
    GET http://localhost:8080/api/products/stats/prices
    GET http://localhost:8080/api/products/stats/attributes
    

十、高级特性与优化

1. 分词器配置

application.properties中添加自定义分词器配置:

# 自定义分词器配置
spring.elasticsearch.rest.connection-timeout=10000
spring.elasticsearch.rest.read-timeout=30000
2. 性能优化
  • 批量操作:使用BulkRequest批量导入数据
  • 预热缓存:对热门查询进行预加载
  • 索引分片优化:根据数据量调整分片数和副本数
3. 监控与调优
  • 使用 Elasticsearch 提供的监控 API:

    java

    public ClusterHealthResponse checkClusterHealth() {
        ClusterHealthRequest request = new ClusterHealthRequest();
        try {
            return client.cluster().health(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException("检查集群健康状态失败", e);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值