Option值参与搜索功能文档
功能概述
实现了让子SKU的option值(option1_value, option2_value, option3_value)参与搜索的功能。
新架构说明:基于简洁版配置架构,索引结构由 mappings/search_products.json 定义,搜索行为由 config/config.yaml 配置。
改动清单
1. 索引Mapping (mappings/search_products.json)
添加3个新字段用于存储去重后的option值:
{
"mappings": {
"properties": {
"option1_values": {
"type": "keyword"
},
"option2_values": {
"type": "keyword"
},
"option3_values": {
"type": "keyword"
}
}
}
}
2. 配置文件 (config/config.yaml)
新增字段权重配置
# 字段权重配置
field_boosts:
# ... 其他字段 ...
option1_values: 0.5
option2_values: 0.5
option3_values: 0.5
将新字段加入搜索域
indexes:
- name: "default"
label: "默认搜索"
fields:
- "title_zh"
- "brief_zh"
# ... 其他字段 ...
- "option1_values"
- "option2_values"
- "option3_values"
boost: 1.0
新增SPU配置项
spu_config:
enabled: true
spu_field: "spu_id"
inner_hits_size: 10
# 配置哪些option维度参与检索(进索引、以及在线搜索)
# 格式为list,选择option1/option2/option3中的一个或多个
searchable_option_dimensions: ['option1', 'option2', 'option3']
3. 配置加载器 (config/config_loader.py)
SPUConfig类扩展
@dataclass
class SPUConfig:
enabled: bool = False
spu_field: Optional[str] = None
inner_hits_size: int = 3
searchable_option_dimensions: List[str] = field(
default_factory=lambda: ['option1', 'option2', 'option3']
)
配置解析逻辑
spu_config = SPUConfig(
enabled=spu_data.get("enabled", False),
spu_field=spu_data.get("spu_field"),
inner_hits_size=spu_data.get("inner_hits_size", 3),
searchable_option_dimensions=spu_data.get(
"searchable_option_dimensions",
['option1', 'option2', 'option3']
)
)
4. 数据灌入模块 (indexer/spu_transformer.py)
加载配置
def __init__(self, db_engine: Any, tenant_id: str):
self.db_engine = db_engine
self.tenant_id = tenant_id
# 加载配置获取searchable_option_dimensions
try:
config_loader = ConfigLoader()
config = config_loader.load_config()
self.searchable_option_dimensions = config.spu_config.searchable_option_dimensions
except Exception as e:
print(f"Warning: Failed to load config, using default: {e}")
self.searchable_option_dimensions = ['option1', 'option2', 'option3']
提取option值逻辑
# 从子SKU提取option值
option1_values = []
option2_values = []
option3_values = []
for _, sku_row in skus.iterrows():
if pd.notna(sku_row.get('option1')):
option1_values.append(str(sku_row['option1']))
if pd.notna(sku_row.get('option2')):
option2_values.append(str(sku_row['option2']))
if pd.notna(sku_row.get('option3')):
option3_values.append(str(sku_row['option3']))
# 去重并根据配置决定是否写入索引
if 'option1' in self.searchable_option_dimensions:
doc['option1_values'] = list(set(option1_values)) if option1_values else []
else:
doc['option1_values'] = []
# option2和option3类似...
5. 在线搜索
无需修改代码!
现有的 get_match_fields_for_index 机制会自动:
- 从
field_boosts读取字段权重 - 将配置中的字段加入multi_match的fields
- 应用配置的权重(0.5)
使用说明
配置方式
在 config/config.yaml 中修改 searchable_option_dimensions:
# 所有option都参与检索
searchable_option_dimensions: ['option1', 'option2', 'option3']
# 只有option1参与检索
searchable_option_dimensions: ['option1']
# option1和option3参与检索
searchable_option_dimensions: ['option1', 'option3']
权重调整
在 config/config.yaml 的 field_boosts 中修改:
field_boosts:
option1_values: 0.8 # 调整为0.8
option2_values: 0.5
option3_values: 0.5
数据灌入流程
方案1:完整重建索引
python scripts/recreate_and_import.py \
--tenant-id 1 \
--recreate \
--db-host localhost \
--db-database saas \
--db-username root \
--db-password xxx
方案2:单独灌入数据
python scripts/ingest_shoplazza.py \
--tenant-id 1 \
--db-host localhost \
--db-database saas \
--db-username root \
--db-password xxx
注意:如果修改了mapping(添加新字段),需要先重建索引。
测试验证
1. 验证数据是否正确写入
使用ES查询检查文档:
curl -X GET "localhost:9200/search_products/_search?pretty" \
-H 'Content-Type: application/json' -d'
{
"query": {"match_all": {}},
"size": 1,
"_source": ["spu_id", "title_zh", "option1_values", "option2_values", "option3_values"]
}
'
期望结果:
{
"hits": {
"hits": [
{
"_source": {
"spu_id": "123",
"title_zh": "测试商品",
"option1_values": ["红色", "蓝色", "绿色"],
"option2_values": ["S", "M", "L"],
"option3_values": []
}
}
]
}
}
2. 验证option值参与搜索
假设某个商品有子SKU的option1值为 "红色"、"蓝色":
# 搜索"红色"应该能匹配到该商品
curl -X POST "localhost:9200/search_products/_search?pretty" \
-H 'Content-Type: application/json' -d'
{
"query": {
"multi_match": {
"query": "红色",
"fields": ["title_zh^3.0", "option1_values^0.5"]
}
}
}
'
3. 通过API测试
curl -X POST "http://localhost:6002/api/search" \
-H "Content-Type: application/json" \
-d '{
"query": "红色",
"tenant_id": "1",
"size": 10
}'
期望:搜索"红色"能匹配到option1_value包含"红色"的商品。
设计亮点
1. 配置驱动
通过配置文件灵活控制哪些option参与检索,无需修改代码:
searchable_option_dimensions: ['option1'] # 配置即可
2. 权重集中管理
所有字段权重统一在 field_boosts 中配置,便于调整:
field_boosts:
title_zh: 3.0
option1_values: 0.5
# 集中管理,一目了然
3. 复用现有框架
充分利用现有的 get_match_fields_for_index 机制:
- 自动从
field_boosts读取权重 - 自动将字段加入搜索
- 无需额外开发
4. 最小改动
只修改了必要的模块:
- ✅ 添加mapping字段
- ✅ 添加配置项
- ✅ 修改数据灌入逻辑
- ❌ 无需修改搜索逻辑(自动支持)
5. 向后兼容
默认配置包含所有option,不影响现有功能:
searchable_option_dimensions: ['option1', 'option2', 'option3'] # 默认全部
架构优势
简洁版配置架构
本功能基于新的简洁版配置架构实现:
| 组件 | 职责 | 优势 |
|---|---|---|
mappings/search_products.json |
定义索引结构 | 单一真相来源 |
config/config.yaml |
定义搜索行为 | 简洁易读 |
field_boosts |
字段权重字典 | 集中管理 |
与旧架构对比
旧架构:需要在 config.yaml 中详细定义字段类型、analyzer等。
新架构:只需配置权重,字段结构由mapping定义。
# 新架构 - 只配置权重
field_boosts:
option1_values: 0.5
vs
# 旧架构 - 需要详细定义(已废弃)
fields:
- name: "option1_values"
type: "KEYWORD"
boost: 0.5
index: true
store: true
# ... 更多配置
注意事项
1. 索引重建
修改mapping后需要重建索引:
python scripts/recreate_and_import.py --tenant-id 1 --recreate --db-xxx
2. 配置验证
修改配置后建议验证:
from config import ConfigLoader
loader = ConfigLoader()
config = loader.load_config(validate=True) # 自动验证
3. 权重调优
初始权重设为0.5,可根据实际效果调整:
field_boosts:
option1_values: 0.8 # 提高权重
option2_values: 0.3 # 降低权重
4. 空值处理
未配置的option字段会写入空数组,不影响搜索:
# 如果只配置 ['option1']
doc['option1_values'] = ["红色", "蓝色"] # 有值
doc['option2_values'] = [] # 空数组
doc['option3_values'] = [] # 空数组
故障排查
1. option值没有进入索引
检查项:
- ✅
searchable_option_dimensions配置是否正确 - ✅ 数据灌入日志是否有警告信息
- ✅ MySQL中的SKU数据option字段是否有值
- ✅ 是否已重建索引
解决方案:
# 查看灌入日志
python scripts/ingest_shoplazza.py --tenant-id 1 --db-xxx
# 检查配置
python -c "from config import ConfigLoader; print(ConfigLoader().load_config().spu_config.searchable_option_dimensions)"
2. 搜索option值没有效果
检查项:
- ✅ 字段是否在
default索引域的fields列表中 - ✅ 权重是否设置正确(不为0)
- ✅ 使用ES的
_analyzeAPI 检查分词
解决方案:
# 确保字段在搜索域中
indexes:
- name: "default"
fields:
- "option1_values" # 必须包含
# 确保权重合理
field_boosts:
option1_values: 0.5 # 不要设为0
3. 配置加载失败
检查项:
- ✅
config/config.yaml语法是否正确 - ✅ 查看应用启动日志
解决方案:
# 验证YAML语法
python -c "import yaml; yaml.safe_load(open('config/config.yaml'))"
# 测试配置加载
python -c "from config import ConfigLoader; ConfigLoader().load_config()"
性能影响
索引大小
每个SPU增加3个keyword数组字段,预估增加:
- 小数据集(
- 中数据集(10k-100k SPU):约5-10%
- 大数据集(>100k SPU):需要监控
搜索性能
- option_values字段为keyword类型,精确匹配,性能良好
- 权重设为0.5,对相关性影响较小
- 建议监控查询延迟并根据实际情况调整
扩展建议
1. 动态权重
未来可支持根据用户行为动态调整权重:
field_boosts:
option1_values: ${dynamic.option1_weight} # 动态权重
2. 多语言option
支持option值的多语言搜索:
field_boosts:
option1_values_zh: 0.5
option1_values_en: 0.5
3. option分组
支持按option分组聚合:
facets:
- field: "option1_values"
type: "terms"
功能版本: v1.0
文档日期: 2024-12-02
架构版本: v2.0 (简洁版配置架构)