
在电商业务中,商品搜索是核心功能之一 ,其体验直接影响用户转化率。本文以 图灵商城商品搜索项目 为蓝本,系统拆解如何基于 Elasticsearch(ES) 构建一个高可用、高性能、高体验的仿京东商品搜索服务,涵盖 业务场景分析 → 文档建模 → DSL 查询构建 → Spring Boot 微服务落地 全流程,提供可直接复用的技术方案。
🔍 一、核心业务场景分析
图灵商城的商品搜索对标 京东搜索体验,需支持以下六大核心能力:
- 关键字模糊查询
- 支持对商品名称、关键词、副标题进行 中文分词检索;
- 多条件精准筛选
- 可按品牌、分类、商品属性(如 CPU、颜色、尺寸)、价格区间、库存状态等进行组合筛选;
- 自定义排序
- 支持按销量、价格、上架时间进行 升序 / 降序 排序;
- 聚合统计展示
- 搜索结果页实时聚合可选的品牌、分类、属性,实现 "边搜边筛" 的交互体验;
- 高亮显示
- 对命中关键词进行 红色高亮,提升用户视觉感知;
- 分页加载
- 支持分页查询,保障系统性能与响应速度。
🧱 二、Elasticsearch 商品文档建模
2.1 核心设计原则
为兼顾 检索效率、字段语义、关联准确性,建模时遵循以下原则:
| 设计要点 | 技术实现 |
|---|---|
| 中文文本字段 | 使用**ik_max_word 分词器**,支持细粒度分词(如 "小米 11 手机" → [小米, 11, 手机]) |
| 精确匹配字段 | 定义为**keyword 类型**(如品牌名、分类名),避免分词干扰 |
| 多值动态属性 | 采用**nested 嵌套类型**,解决属性扁平化导致的"虚假匹配"问题 |
| 数值/布尔字段 | 使用 double、long、boolean 等原生类型,支持范围查询与排序 |
| 日期字段 | 定义为 date 类型,便于按上架时间排序 |
💡 为什么用
nested?若将属性数组扁平化存储(如
attrName: ["cpu", "颜色"],attrValue: ["2核", "黑色"]),ES 会丢失属性间的关联性,导致 "CPU=2 核 且 颜色=白色" 错误匹配到 "CPU=2 核 + 黑色" 和 "CPU=4 核 + 白色" 的商品。**nested类型保留对象完整性**,确保多条件组合筛选精准无误。
2.2 商品文档结构示例
json
{
"id": "26",
"name": "小米 11 手机",
"keywords": "小米手机",
"subTitle": "AI智慧全面屏 6GB+64GB 亮黑色 全网通版 4G手机",
"price": 3999.0,
"promotionPrice": "2999",
"originalPrice": "5999",
"pic": "<http://xxx/xiaomi.jpg>",
"sale": 999,
"hasStock": true,
"salecount": 999,
"putawayDate": "2021-04-01",
"brandId": 6,
"brandName": "小米",
"categoryId": 19,
"categoryName": "手机通讯",
"attrs": [
{ "attrId": 1, "attrName": "cpu", "attrValue": "2核" },
{ "attrId": 2, "attrName": "颜色", "attrValue": "黑色" }
]
}
2.3 索引 Mapping 定义(product_db)
json
PUT product_db
{
"mappings": {
"properties": {
"id": {"type": "long"},
"name": {"type": "text", "analyzer": "ik_max_word"},
"keywords": {"type": "text", "analyzer": "ik_max_word"},
"subTitle": {"type": "text", "analyzer": "ik_max_word"},
"price": {"type": "double"},
"promotionPrice": {"type": "keyword"},
"originalPrice": {"type": "keyword"},
"pic": {"type": "keyword"},
"salecount": {"type": "long"},
"putawayDate": {"type": "date"},
"hasStock": {"type": "boolean"},
"brandId": {"type": "long"},
"brandName": {"type": "keyword"},
"brandImg": {"type": "keyword"},
"categoryId": {"type": "long"},
"categoryName": {"type": "keyword"},
"attrs": {
"type": "nested",
"properties": {
"attrId": {"type": "long"},
"attrName": {"type": "keyword"},
"attrValue": {"type": "keyword"}
}
}
}
}
}
✅ 关键点:
name/keywords/subTitle使用ik_max_word实现 高召回率;brandName/categoryName/attrValue使用keyword保证 筛选精确性;attrs为nested,支撑 多属性 AND 组合查询。
2.4 测试数据准备
导入 17 条多品类商品数据,覆盖:
- 品牌:小米、华为、苹果、海澜之家、耐克
- 类目:手机、T 恤、电视、运动鞋、笔记本
- 属性:CPU、颜色、尺寸、容量等
为后续 DSL 测试与微服务验证提供数据基础。
🔎 三、Elasticsearch DSL 查询语句构建
3.1 核心能力拆解
| 功能 | DSL 实现方式 |
|---|---|
| 多字段模糊搜索 | multi_match 覆盖 name/keywords/subTitle |
| 精准过滤 | filter 子句(品牌、分类、库存、属性) |
| 价格区间 | range 查询(gte/lte) |
| 嵌套属性筛选 | nested 查询 +term 匹配 |
| 排序 | sort 按 salecount/price/putawayDate |
| 聚合统计 | terms 聚合 +nested 聚合 |
| 高亮 | highlight+ 自定义 `` 标签 |
| 分页 | from+size |
⚠️ 性能提示:
所有过滤条件(不参与评分)应放入
filter而非must,利用 BitSet 缓存 提升性能。
3.2 完整 DSL 示例
场景:搜索 "手机",价格 2000--5000 元、有库存,按销量降序,聚合品牌/分类/属性,高亮关键词。
json
GET product_db/_search
{
"from": 0,
"size": 20,
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "手机",
"fields": ["name", "keywords", "subTitle"]
}
}
],
"filter": [
{ "term": { "hasStock": true } },
{ "range": { "price": { "gte": 2000, "lte": 5000 } } }
]
}
},
"aggs": {
"brandId_aggs": {
"terms": { "field": "brandId", "size": 10 },
"aggs": {
"brandName_aggs": { "terms": { "field": "brandName" } },
"brandImg_aggs": { "terms": { "field": "brandImg" } }
}
},
"categoryId_aggs": {
"terms": { "field": "categoryId", "size": 10 },
"aggs": {
"categoryName_aggs": { "terms": { "field": "categoryName" } }
}
},
"attrs_aggs": {
"nested": { "path": "attrs" },
"aggs": {
"attrId_aggs": {
"terms": { "field": "attrs.attrId" },
"aggs": {
"attrName_aggs": { "terms": { "field": "attrs.attrName" } },
"attrValue_aggs": { "terms": { "field": "attrs.attrValue" } }
}
}
}
}
},
"sort": [{ "salecount": { "order": "desc" } }],
"highlight": {
"pre_tags": ["<b style='color:red'>"],
"post_tags": ["</b>"],
"fields": { "name": {} }
}
}
⚙️ 四、Spring Boot 微服务落地实现
4.1 引入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
4.2 核心代码逻辑
4.2.1 构建搜索请求(startBuildRequestParam)
- Bool 查询组装 :
must:multi_match关键字filter:品牌、分类、库存、价格、属性(nested)
- 价格解析 :支持
10_5000(区间)、_5000(≤5000)、10_(≥10) - 属性筛选 :解析
attrs=1_2核&2_蓝色→ 构建多个nested查询 - 排序 & 分页 & 高亮:标准化处理
4.2.2 执行检索
java
SearchResponse<EsProduct> response = client.search(searchRequest, EsProduct.class);
4.2.3 封装响应(startBuildResponseResult)
- 解析商品列表,替换高亮字段
- 解析聚合结果 →
BrandVo/CategoryVo/AttrVo - 计算总页数、当前页、分页导航
4.3 接口测试示例
GET <http://localhost:8054/searchList>?
keyword=手机&
price=1_5000&
hasStock=1&
categoryId=19&
attrs=2_蓝色&
attrs=1_2核&
sort=salecount_asc&
pageNum=1&
pageSize=20
✅ 支持 任意维度组合查询,参数灵活可扩展。
🌟 五、核心技术亮点与总结
5.1 技术亮点
| 亮点 | 说明 |
|---|---|
| 精准建模 | 合理使用 text/keyword/nested/date,解决电商核心痛点 |
| 高性能查询 | filter+nested 保证筛选精准性与查询效率 |
| 高体验交互 | 高亮 + 聚合 + 排序 → 还原京东式搜索体验 |
| 工程化落地 | 参数化解析 →DSL → Java 对象,代码清晰可维护 |
| 全场景覆盖 | 支持价格多格式、属性多组合、排序多维度、分页自定义 |
5.2 项目总结
本项目完整实现了 从需求到上线 的电商搜索闭环:
业务驱动 → 模型设计 → 查询优化 → 服务封装
该方案具备以下优势:
- ✅ 可直接复用于实际电商项目
- ✅ 易于扩展:可叠加拼音搜索、同义词、热度排序、个性化推荐
- ✅ 可水平扩展:配合 ES 集群,支撑高并发场景
未来演进方向:
- 引入 NLP 语义理解 提升搜索相关性
- 结合 用户行为日志 实现个性化排序
- 使用 向量检索 支持"以图搜商品"
📌 结语:
搜索不仅是"找得到",更是"找得准、找得快、找得爽"。
通过 Elasticsearch + Spring Boot 的深度整合,我们打造了一个 高性能、高体验、高可用 的电商搜索引擎,为业务增长提供坚实底座。