# 分面数据问题诊断报告 ## 问题描述 前端显示的分面结果都是空的: - Category: 空 - Color: 空 - Size: 空 - Material: 空 ES的聚合查询结果也是空的。 ## 数据流程分析 ### 1. 数据生成阶段(csv_to_excel_multi_variant.py) **生成的数据**: #### 分类信息: - Excel字段:`'专辑名称': csv_data['categoryName']` - 示例值:`"电子产品"` 或 `"服装/男装"`(从CSV的categoryName字段读取) #### 属性信息(M+P类型商品): - Excel字段(M行主商品): - `'款式1': 'color'`(选项名称) - `'款式2': 'size'`(选项名称) - `'款式3': 'material'`(选项名称) - Excel字段(P行子款式): - `'款式1': 'Red'`(选项值,从COLORS列表随机选择) - `'款式2': '5'`(选项值,1-30随机选择) - `'款式3': '塑料'`(选项值,从商品标题提取) ### 2. Excel导入店匠系统 → MySQL **预期映射**: #### 分类字段: - Excel `'专辑名称'` → MySQL `shoplazza_product_spu.category_path` **或** `category` 字段 - **问题**:店匠系统可能将"专辑名称"映射到`category`字段,而不是`category_path`字段 #### 属性字段: - Excel `'款式1/2/3'`(M行)→ MySQL `shoplazza_product_option.name` 和 `position` - Excel `'款式1/2/3'`(P行)→ MySQL `shoplazza_product_sku.option1/2/3` ### 3. MySQL → ES转换阶段(spu_transformer.py) #### category1_name 构建逻辑(第228-240行): ```python if pd.notna(spu_row.get('category_path')): category_path = str(spu_row['category_path']) # 解析category_path获取多层级分类名称 path_parts = category_path.split('/') if len(path_parts) > 0: doc['category1_name'] = path_parts[0].strip() ``` **问题**:如果MySQL中的`category_path`字段为空,`category1_name`不会被设置! #### specifications 构建逻辑(第328-347行): ```python # 构建option名称映射(position -> name) option_name_map = {} if not options.empty: for _, opt_row in options.iterrows(): position = opt_row.get('position') name = opt_row.get('name') if pd.notna(position) and pd.notna(name): option_name_map[int(position)] = str(name) # 构建specifications if pd.notna(sku_row.get('option1')) and 1 in option_name_map: specifications.append({ 'sku_id': sku_id, 'name': option_name_map[1], # 使用option表的name字段 'value': str(sku_row['option1']) }) ``` **问题**:如果`shoplazza_product_option`表中没有记录,或者`name`字段值不是英文(如"color"),会导致: 1. `option_name_map`为空,无法构建specifications 2. 即使有值,如果name不是"color"/"size"/"material",前端也无法正确匹配 ## 问题根源 ### 问题1:category1_name 为空 **原因**: 1. MySQL的`category_path`字段可能为空 2. Excel的"专辑名称"可能被映射到`category`字段而不是`category_path` 3. 如果`category_path`为空,`category1_name`不会被设置 **验证方法**: ```sql SELECT COUNT(*) as total, COUNT(category_path) as has_category_path, COUNT(category) as has_category FROM shoplazza_product_spu WHERE tenant_id = 162 AND deleted = 0; ``` ### 问题2:specifications 为空 **原因**: 1. `shoplazza_product_option`表可能没有数据 2. option表的`name`字段值可能不是英文(不是"color"、"size"、"material") **验证方法**: ```sql SELECT DISTINCT name, position, COUNT(*) as count FROM shoplazza_product_option WHERE tenant_id = 162 AND deleted = 0 GROUP BY name, position ORDER BY position, name; ``` ## 解决方案 ### 方案1:修复 spu_transformer.py - 支持从category字段生成category1_name 修改`indexer/spu_transformer.py`的`_transform_spu_to_doc`方法,如果`category_path`为空,使用`category`字段作为备选: ```python # Category相关字段 if pd.notna(spu_row.get('category_path')): category_path = str(spu_row['category_path']) doc['category_path_zh'] = category_path doc['category_path_en'] = None # 解析category_path获取多层级分类名称 path_parts = category_path.split('/') if len(path_parts) > 0: doc['category1_name'] = path_parts[0].strip() if len(path_parts) > 1: doc['category2_name'] = path_parts[1].strip() if len(path_parts) > 2: doc['category3_name'] = path_parts[2].strip() elif pd.notna(spu_row.get('category')): # 如果category_path为空,使用category字段作为category1_name category = str(spu_row['category']) doc['category1_name'] = category.strip() # 如果category包含"/",也尝试解析 if '/' in category: path_parts = category.split('/') if len(path_parts) > 0: doc['category1_name'] = path_parts[0].strip() if len(path_parts) > 1: doc['category2_name'] = path_parts[1].strip() if len(path_parts) > 2: doc['category3_name'] = path_parts[2].strip() ``` ### 方案2:检查并修复 option 表的 name 字段值 需要确保`shoplazza_product_option`表的`name`字段值是英文: - position=1 的name应该是 `"color"` - position=2 的name应该是 `"size"` - position=3 的name应该是 `"material"` 如果值不对,需要更新: ```sql -- 查看当前的name值 SELECT DISTINCT name, position FROM shoplazza_product_option WHERE tenant_id = 162 AND deleted = 0 ORDER BY position; -- 如果需要更新(示例) -- UPDATE shoplazza_product_option -- SET name = CASE position -- WHEN 1 THEN 'color' -- WHEN 2 THEN 'size' -- WHEN 3 THEN 'material' -- END -- WHERE tenant_id = 162 AND deleted = 0; ``` ### 方案3:验证数据完整性 使用诊断脚本检查数据: ```bash python scripts/check_data_source.py \ --tenant-id 162 \ --db-host \ --db-port 3316 \ --db-database saas \ --db-username saas \ --db-password ``` ## 诊断步骤 ### 步骤1:检查MySQL数据 运行诊断脚本: ```bash cd /home/tw/SearchEngine source /home/tw/miniconda3/etc/profile.d/conda.sh conda activate searchengine python scripts/check_data_source.py --tenant-id 162 --db-host --db-database saas --db-username saas --db-password ``` ### 步骤2:根据检查结果修复 #### 如果 category_path 为空: - 使用方案1:修改`spu_transformer.py`支持从`category`字段生成`category1_name` #### 如果 option 表没有数据或name值不对: - 检查Excel导入是否正确 - 如果需要,手动更新option表的name字段值 ### 步骤3:重新导入数据到ES ```bash python scripts/recreate_and_import.py \ --tenant-id 162 \ --db-host \ --db-database saas \ --db-username saas \ --db-password \ --es-host http://localhost:9200 ``` ### 步骤4:验证ES数据 检查ES索引中的文档: ```bash curl -X GET "http://localhost:9200/search_products/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "term": { "tenant_id": "162" } }, "size": 1, "_source": ["spu_id", "title_zh", "category1_name", "specifications", "option1_name"] }' ``` ## 预期结果 修复后,ES文档应该包含: 1. **category1_name字段**: ```json { "category1_name": "电子产品" } ``` 2. **specifications字段**: ```json { "specifications": [ {"sku_id": "123", "name": "color", "value": "Red"}, {"sku_id": "123", "name": "size", "value": "5"}, {"sku_id": "123", "name": "material", "value": "塑料"} ] } ``` 3. **option1_name/2_name/3_name字段**: ```json { "option1_name": "color", "option2_name": "size", "option3_name": "material" } ``` ## 总结 问题可能出现在: 1. **MySQL数据层面**:`category_path`字段为空,或者`shoplazza_product_option`表没有正确的数据 2. **数据转换层面**:`spu_transformer.py`没有处理`category_path`为空的情况 建议先运行诊断脚本检查MySQL数据,然后根据检查结果进行修复。