Elasticsearch 全文搜索实战:构建企业级搜索引擎

Elasticsearch 全文搜索实战:构建企业级搜索引擎

别叫我大神,叫我 Alex 就好。搜索是用户体验的关键,Elasticsearch 让全文搜索变得简单而强大。

一、Elasticsearch 基础配置

1.1 Spring Data Elasticsearch 配置

java 复制代码
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.example.repository")
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {

    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        ClientConfiguration clientConfiguration = ClientConfiguration.builder()
            .connectedTo("localhost:9200")
            .withConnectTimeout(Duration.ofSeconds(5))
            .withSocketTimeout(Duration.ofSeconds(30))
            .withBasicAuth("elastic", "password")
            .build();

        return RestClients.create(clientConfiguration).rest();
    }

    @Bean
    public ElasticsearchOperations elasticsearchTemplate() {
        return new ElasticsearchRestTemplate(elasticsearchClient());
    }
}

1.2 文档映射

java 复制代码
@Document(indexName = "products", createIndex = false)
@Setting(settingPath = "elasticsearch/products-settings.json")
@Mapping(mappingPath = "elasticsearch/products-mapping.json")
public class Product {

    @Id
    private String id;

    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
    private String name;

    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String description;

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

    @Field(type = FieldType.Double)
    private BigDecimal price;

    @Field(type = FieldType.Integer)
    private Integer stock;

    @Field(type = FieldType.Date, format = DateFormat.date_time)
    private LocalDateTime createTime;

    @Field(type = FieldType.Nested)
    private List<ProductAttribute> attributes;

    // Getters and setters
}

// 嵌套对象
public class ProductAttribute {

    @Field(type = FieldType.Keyword)
    private String name;

    @Field(type = FieldType.Keyword)
    private String value;
}

1.3 索引设置

json 复制代码
// products-settings.json
{
  "index": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "analysis": {
      "analyzer": {
        "ik_custom": {
          "type": "custom",
          "tokenizer": "ik_max_word",
          "filter": ["lowercase", "synonym_filter"]
        }
      },
      "filter": {
        "synonym_filter": {
          "type": "synonym",
          "synonyms": ["手机,移动电话,智能手机", "电脑,计算机,笔记本"]
        }
      }
    }
  }
}

// products-mapping.json
{
  "properties": {
    "name": {
      "type": "text",
      "analyzer": "ik_max_word",
      "fields": {
        "keyword": {
          "type": "keyword",
          "ignore_above": 256
        },
        "suggest": {
          "type": "completion"
        }
      }
    },
    "description": {
      "type": "text",
      "analyzer": "ik_max_word"
    },
    "category": {
      "type": "keyword"
    },
    "price": {
      "type": "scaled_float",
      "scaling_factor": 100
    },
    "attributes": {
      "type": "nested"
    }
  }
}

二、搜索功能实现

2.1 基础搜索

java 复制代码
@Repository
public interface ProductRepository extends ElasticsearchRepository<Product, String> {

    // 简单查询
    List<Product> findByNameContaining(String name);

    // 分页查询
    Page<Product> findByCategory(String category, Pageable pageable);

    // 范围查询
    List<Product> findByPriceBetween(BigDecimal minPrice, BigDecimal maxPrice);

    // 多字段查询
    @Query("{\"multi_match\": {\"query\": \"?0\", \"fields\": [\"name^3\", \"description\"]}}")
    List<Product> searchByKeyword(String keyword);
}

2.2 高级搜索服务

java 复制代码
@Service
public class ProductSearchService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchTemplate;

    public SearchHits<Product> advancedSearch(ProductSearchRequest request) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // 关键词搜索
        if (StringUtils.hasText(request.getKeyword())) {
            MultiMatchQueryBuilder multiMatchQuery = QueryBuilders
                .multiMatchQuery(request.getKeyword(), "name^3", "description", "category")
                .type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
                .fuzziness(Fuzziness.AUTO);

            boolQuery.must(multiMatchQuery);
        }

        // 分类过滤
        if (StringUtils.hasText(request.getCategory())) {
            boolQuery.filter(QueryBuilders.termQuery("category", request.getCategory()));
        }

        // 价格范围
        if (request.getMinPrice() != null || request.getMaxPrice() != null) {
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            if (request.getMinPrice() != null) {
                rangeQuery.gte(request.getMinPrice());
            }
            if (request.getMaxPrice() != null) {
                rangeQuery.lte(request.getMaxPrice());
            }
            boolQuery.filter(rangeQuery);
        }

        // 属性过滤
        if (request.getAttributes() != null && !request.getAttributes().isEmpty()) {
            for (Map.Entry<String, String> attr : request.getAttributes().entrySet()) {
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery(
                    "attributes",
                    QueryBuilders.boolQuery()
                        .must(QueryBuilders.termQuery("attributes.name", attr.getKey()))
                        .must(QueryBuilders.termQuery("attributes.value", attr.getValue())),
                    ScoreMode.None
                );
                boolQuery.filter(nestedQuery);
            }
        }

        // 构建查询
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder()
            .withQuery(boolQuery)
            .withPageable(PageRequest.of(request.getPage(), request.getSize()));

        // 排序
        if (StringUtils.hasText(request.getSortField())) {
            SortOrder sortOrder = "desc".equalsIgnoreCase(request.getSortOrder())
                ? SortOrder.DESC : SortOrder.ASC;
            queryBuilder.withSort(SortBuilders.fieldSort(request.getSortField()).order(sortOrder));
        }

        // 高亮
        queryBuilder.withHighlightFields(
            new HighlightBuilder.Field("name").preTags("<em>").postTags("</em>"),
            new HighlightBuilder.Field("description").preTags("<em>").postTags("</em>")
        );

        // 聚合
        queryBuilder.addAggregation(
            AggregationBuilders.terms("categories").field("category").size(20)
        );
        queryBuilder.addAggregation(
            AggregationBuilders.histogram("price_ranges").field("price").interval(100)
        );

        NativeSearchQuery searchQuery = queryBuilder.build
相关推荐
云-缩影6 分钟前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
瑞克-桑切斯27 分钟前
7、MySQL 慢查询排查与优化实战指南
java·服务器·数据库
Flynn Liang4 小时前
AI 应用的安全红线:Spring AI 下的 Prompt 注入防护、PII 脱敏、输出审计与密钥管理
人工智能·安全·spring·数据泄露·springai·prompt注入
QCoding4 小时前
企业级Agent Evaluation实战:Agent能跑就能上线?AI Agent到底怎么测?
后端·spring
海的透彻5 小时前
dockerfile如何快速构建容器(java服务)
java·开发语言
code2cat5 小时前
Java工具篇之Gradle:用任务图组织构建与交付
java·开发语言·单元测试·gradle
后台模板学习6 小时前
大模型 Prompt 工程在电商订单系统中的踩坑记录
java·python·prompt
w_zero_one6 小时前
算法--构造
java·数据结构·算法
SL_staff6 小时前
制造业订单交付崩盘?用四层协同框架重建交付可信度(技术实践)
java·产品·全栈
SL_staff7 小时前
打破风控迭代死循环:用决策编排重构业务与技术协作链路
java·产品·全栈