第25章:集成Elasticsearch实现商品检索
本文介绍了如何利用Elasticsearch构建电商平台的商品搜索服务。首先对比了Elasticsearch与关系型数据库的核心概念,重点解析了倒排索引的搜索机制。接着详细说明了将MySQL商品数据同步到Elasticsearch的实现方案,包括创建搜索服务、配置ES连接、定义数据模型和编写同步接口。最后阐述了如何实现复杂的商品检索功能,通过Bool Query组合多种查询条件,支持关键字搜索、分
电商平台的搜索功能是用户找到商品最直接、最高效的途径,其性能和相关性直接影响用户体验和转化率。传统的数据库LIKE查询在海量数据面前性能低下且功能有限。Elasticsearch(简称ES)是一个功能强大、基于Lucene的开源搜索引擎,它提供了近实时的搜索、强大的聚合分析能力和水平扩展性。本章我们将构建一个专门的搜索服务cloudmall-search,将商品数据同步到ES中,并实现复杂的商品检索功能。
25.1 Elasticsearch核心概念
内容讲解
为了更好地使用ES,我们需要理解它的一些核心概念,并将其与关系型数据库进行类比:
| Elasticsearch | 关系型数据库 (MySQL) |
|---|---|
| Index (索引) | Database (数据库) |
| Type (类型) | Table (表) - 在ES 7.x后被弱化,一个Index只有一个_doc类型 |
| Document (文档) | Row (行) - ES中的数据单元,以JSON格式表示 |
| Field (字段) | Column (列) - Document中的一个字段 |
| Mapping (映射) | Schema (表结构) - 定义Index中字段的类型、分词器等属性 |
| Inverted Index (倒排索引) | 无直接对应 - ES实现快速搜索的核心机制 |
倒排索引 (Inverted Index)
这是ES实现全文检索的关键。传统的关系型数据库是“文档 -> 词”的正向索引,而倒排索引是“词 -> 文档”的结构。它记录了每个词(Term)出现在哪些文档(Document)中,以及出现的位置和频率。当用户搜索一个词时,ES可以直接通过倒排索引找到所有包含该词的文档,从而极大地提高了搜索速度。
25.2 商品数据同步到ES
内容讲解
要实现搜索,首先需要将商品数据从MySQL数据库同步到Elasticsearch中。这个过程称为“索引数据”。
实现思路:
- 创建
cloudmall-search服务: 引入spring-boot-starter-data-elasticsearch依赖。 - 配置ES连接: 在
application.yml中配置ES集群的地址。 - 定义ES数据模型: 创建一个
ProductEsModel.java类,其字段对应我们需要在ES中存储和检索的商品信息。使用@Document注解标记这是一个ES文档模型。 - 编写数据同步接口: 在
cloudmall-search服务中提供一个HTTP接口,当调用该接口时,它会:
a. 通过Feign远程调用cloudmall-product服务,获取所有已上架(status=1)的SKU信息。
b. 将查询到的SKU列表转换成ProductEsModel列表。
c. 使用ElasticsearchRestTemplate的save方法,将数据批量保存到ES中。
代码示例
1. pom.xml依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
2. ProductEsModel.java
package com.cloudmall.search.vo;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
@Document(indexName = "product") // 指定索引名
public class ProductEsModel {
@Id
private Long skuId;
@Field(type = FieldType.Text, analyzer = "ik_smart") // 使用IK分词器
private String skuTitle;
@Field(type = FieldType.Keyword) // Keyword类型不分词
private String skuImg;
@Field(type = FieldType.Double)
private Double skuPrice;
// ... 其他需要检索或展示的字段 ...
}
3. 数据同步Controller (ElasticSaveController.java)
package com.cloudmall.search.controller;
import com.cloudmall.search.service.ProductSaveService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/search/save")
public class ElasticSaveController {
@Autowired
private ProductSaveService productSaveService;
// 上架商品,将商品数据保存到ES
@PostMapping("/product")
public Result productStatusUp(@RequestBody List<ProductEsModel> models) {
boolean success = productSaveService.productStatusUp(models);
if (success) {
return Result.ok();
} else {
return Result.error(BizCodeEnum.PRODUCT_UP_EXCEPTION);
}
}
}
25.3 实现商品检索功能
内容讲解
有了数据之后,我们就可以构建一个复杂的搜索接口,支持多种查询条件。
搜索条件分析:
一个典型的电商搜索页包含:
- 关键字查询: 在商品标题、副标题中进行全文检索。
- 分类过滤: 按商品的三级分类进行筛选。
- 品牌过滤: 按品牌进行筛选。
- 属性过滤: 按商品的销售属性(如颜色、内存)进行筛选。
- 价格区间过滤: 按指定的价格范围筛选。
- 排序: 按销量、价格等字段排序。
- 分页: 返回指定页码的数据。
使用Bool Query构建复合查询
ES的bool查询是构建复杂查询的利器,它包含四个子句:
must: 必须匹配,贡献算分。filter: 必须匹配,但不贡献算分(性能更高,用于精确匹配过滤)。should: 可选匹配,匹配上会增加算分。must_not: 必须不匹配。
我们可以将关键字查询放入must子句,将分类、品牌、属性、价格等过滤条件放入filter子句,从而构建出高效且功能强大的搜索请求。
代码示例 (SearchServiceImpl.java)
package com.cloudmall.search.service.impl;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SearchServiceImpl implements SearchService {
@Autowired
private RestHighLevelClient client;
@Override
public SearchResult search(SearchParam param) {
// 1. 动态构建查询DSL语句
SearchRequest request = buildSearchRequest(param);
// 2. 执行检索
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 3. 将结果封装成VO
SearchResult result = buildSearchResult(response, param);
return result;
}
private SearchRequest buildSearchRequest(SearchParam param) {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 1.1 must - 关键字查询
if (StringUtils.isNotEmpty(param.getKeyword())) {
boolQuery.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
}
// 1.2 filter - 分类过滤
if (param.getCatalog3Id() != null) {
boolQuery.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
}
// 1.3 filter - 品牌过滤 (可多选)
if (param.getBrandId() != null && param.getBrandId().size() > 0) {
boolQuery.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
}
// ... 其他过滤、排序、分页、高亮、聚合等逻辑 ...
sourceBuilder.query(boolQuery);
SearchRequest request = new SearchRequest(new String[]{"product"}, sourceBuilder);
return request;
}
}
本章总结
本章我们引入了强大的搜索引擎Elasticsearch来解决电商平台的核心需求——商品检索。我们首先学习了ES的基本概念,特别是其核心的倒排索引机制。接着,我们从零开始搭建了cloudmall-search服务,设计了商品在ES中的数据模型,并实现了从MySQL到ES的数据同步功能。最核心的部分是,我们分析了复杂的搜索场景,并利用ES的bool查询,将关键字匹配、多维度过滤、排序、分页等需求组合在一起,构建了动态、高效的检索DSL。通过本章的学习,我们不仅掌握了ES的基本使用,更重要的是学会了如何将它与业务场景结合,构建生产级的搜索引擎服务。
资源下载
本章的示例代码可以从以下链接下载:
微服务开发课程第16-26章的源码
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)