Blame view

docs/分面数据问题诊断.md 7.97 KB
a10a89a3   tangwang   构造测试数据用于测试分类 和 三种...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  # 分面数据问题诊断报告
  
  ## 问题描述
  
  前端显示的分面结果都是空的:
  - 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 <mysql_host> \
      --db-port 3316 \
      --db-database saas \
      --db-username saas \
      --db-password <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 <host> --db-database saas --db-username saas --db-password <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 <host> \
      --db-database saas \
      --db-username saas \
      --db-password <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数据,然后根据检查结果进行修复。