OPTION_VALUES_FEATURE.md 10.6 KB

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.yamlfield_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的 _analyze API 检查分词

解决方案

# 确保字段在搜索域中
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 (简洁版配置架构)