# 索引字段说明 v2 本文档详细说明 `search_products` 索引的字段结构、类型、数据来源和用途。 ## 索引概述 - **索引名称**: `search_products` - **索引维度**: SPU(Standard Product Unit)级别 - **多租户隔离**: 通过 `tenant_id` 字段实现 - **Mapping 文件**: `mappings/search_products.json` ## 字段分类 ### 1. 基础标识字段 | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `tenant_id` | keyword | 租户ID,用于多租户隔离 | MySQL: `shoplazza_product_spu.tenant_id` | | `spu_id` | keyword | SPU唯一标识 | MySQL: `shoplazza_product_spu.id` | | `create_time` | date | 创建时间 | MySQL: `shoplazza_product_spu.created_at` | | `update_time` | date | 更新时间 | MySQL: `shoplazza_product_spu.updated_at` | ### 2. 多语言文本字段 所有文本字段都支持中英文双语,后端根据请求的 `language` 参数自动选择对应语言字段返回。 #### 2.1 标题字段 | 字段名 | ES类型 | 分析器 | 说明 | 数据来源 | |--------|--------|--------|------|----------| | `title_zh` | text | hanlp_index / hanlp_standard | 中文标题 | MySQL: `shoplazza_product_spu.title` | | `title_en` | text | english | 英文标题 | 暂为空(待翻译服务填充) | #### 2.2 描述字段 | 字段名 | ES类型 | 分析器 | 说明 | 数据来源 | |--------|--------|--------|------|----------| | `brief_zh` | text | hanlp_index / hanlp_standard | 中文短描述 | MySQL: `shoplazza_product_spu.brief` | | `brief_en` | text | english | 英文短描述 | 暂为空 | | `description_zh` | text | hanlp_index / hanlp_standard | 中文详细描述 | MySQL: `shoplazza_product_spu.description` | | `description_en` | text | english | 英文详细描述 | 暂为空 | #### 2.3 供应商/品牌字段 | 字段名 | ES类型 | 分析器 | 子字段 | 说明 | 数据来源 | |--------|--------|--------|--------|------|----------| | `vendor_zh` | text | hanlp_index / hanlp_standard | `vendor_zh.keyword` (keyword, normalizer: lowercase) | 中文供应商/品牌 | MySQL: `shoplazza_product_spu.vendor` | | `vendor_en` | text | english | `vendor_en.keyword` (keyword, normalizer: lowercase) | 英文供应商/品牌 | 暂为空 | **用途**: - `text` 类型:用于全文搜索(支持模糊匹配) - `keyword` 子字段:用于精确匹配过滤和分面聚合 ### 3. 标签字段 | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `tags` | keyword | 标签列表(数组) | MySQL: `shoplazza_product_spu.tags`(逗号分隔字符串,转换为数组) | **数据格式**: `["新品", "热卖", "爆款"]` ### 4. 类目字段 #### 4.1 类目路径(用于搜索) | 字段名 | ES类型 | 分析器 | 说明 | 数据来源 | |--------|--------|--------|------|----------| | `category_path_zh` | text | hanlp_index / hanlp_standard | 中文类目路径(如"服装/男装/衬衫") | MySQL: `shoplazza_product_spu.category_path` | | `category_path_en` | text | english | 英文类目路径 | 暂为空 | #### 4.2 类目名称(用于搜索) | 字段名 | ES类型 | 分析器 | 说明 | 数据来源 | |--------|--------|--------|------|----------| | `category_name_zh` | text | hanlp_index / hanlp_standard | 中文类目名称 | MySQL: `shoplazza_product_spu.category` | | `category_name_en` | text | english | 英文类目名称 | 暂为空 | #### 4.3 类目标识(用于过滤和分面) | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `category_id` | keyword | 类目ID | MySQL: `shoplazza_product_spu.category_id` | | `category_name` | keyword | 类目名称(用于过滤) | MySQL: `shoplazza_product_spu.category` | | `category_level` | integer | 类目层级(1/2/3) | MySQL: `shoplazza_product_spu.category_level` | | `category1_name` | keyword | 一级类目名称 | 从 `category_path` 解析 | | `category2_name` | keyword | 二级类目名称 | 从 `category_path` 解析 | | `category3_name` | keyword | 三级类目名称 | 从 `category_path` 解析 | **用途**: - `category_path_zh/en`, `category_name_zh/en`: 用于全文搜索,支持模糊匹配 - `category_id`, `category_name`, `category_level`, `category1/2/3_name`: 用于精确过滤和分面聚合 ### 5. 规格字段(Specifications) | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `specifications` | nested | 规格列表(嵌套对象数组) | MySQL: `shoplazza_product_option` + `shoplazza_product_sku.option1/2/3` | **嵌套结构**: ```json { "specifications": [ { "sku_id": "sku_123", "name": "color", "value": "white" }, { "sku_id": "sku_123", "name": "size", "value": "256GB" } ] } ``` **数据来源**: - `name`: 从 `shoplazza_product_option` 表获取(选项名称,如"color"、"size") - `value`: 从 `shoplazza_product_sku` 表的 `option1`, `option2`, `option3` 字段获取(选项值,如"white"、"256GB") - `sku_id`: SKU ID,用于关联 **API 过滤示例**: ```json { "query": "手机", "filters": { "specifications": { "name": "color", "value": "white" } } } ``` **多个规格过滤(按维度分组)**: ```json { "query": "手机", "filters": { "specifications": [ {"name": "color", "value": "white"}, {"name": "size", "value": "256GB"} ] } } ``` 说明:不同维度(不同name)是AND关系,相同维度(相同name)的多个值是OR关系。 **示例:相同维度的多个值(OR)**: ```json { "query": "手机", "filters": { "specifications": [ {"name": "size", "value": "3"}, {"name": "size", "value": "4"}, {"name": "size", "value": "5"}, {"name": "color", "value": "green"} ] } } ``` 生成查询:(size=3 OR size=4 OR size=5) AND color=green **ES 查询结构**(后端自动生成): ```json { "filter": [ { "nested": { "path": "specifications", "query": { "bool": { "should": [ {"bool": {"must": [{"term": {"specifications.name": "size"}}, {"term": {"specifications.value": "3"}}]}}, {"bool": {"must": [{"term": {"specifications.name": "size"}}, {"term": {"specifications.value": "4"}}]}}, {"bool": {"must": [{"term": {"specifications.name": "size"}}, {"term": {"specifications.value": "5"}}]}} ], "minimum_should_match": 1 } } } }, { "nested": { "path": "specifications", "query": { "bool": { "must": [ {"term": {"specifications.name": "color"}}, {"term": {"specifications.value": "green"}} ] } } } } ] } ``` **API 分面示例**: 所有规格名称的分面: ```json { "query": "手机", "facets": ["specifications"] } ``` 指定规格名称的分面: ```json { "query": "手机", "facets": ["specifications.color", "specifications.size"] } ``` **ES 聚合结构**(后端自动生成): 所有规格名称: ```json { "aggs": { "specifications_facet": { "nested": { "path": "specifications" }, "aggs": { "by_name": { "terms": { "field": "specifications.name", "size": 20 }, "aggs": { "value_counts": { "terms": { "field": "specifications.value", "size": 10 } } } } } } } } ``` 指定规格名称: ```json { "aggs": { "specifications_color_facet": { "nested": { "path": "specifications" }, "aggs": { "filter_by_name": { "filter": { "term": { "specifications.name": "color" } }, "aggs": { "value_counts": { "terms": { "field": "specifications.value", "size": 10 } } } } } } } } ``` ### 6. 选项名称字段 | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `option1_name` | keyword | 选项1名称(如"color") | MySQL: `shoplazza_product_option` | | `option2_name` | keyword | 选项2名称(如"size") | MySQL: `shoplazza_product_option` | | `option3_name` | keyword | 选项3名称 | MySQL: `shoplazza_product_option` | ### 7. 价格字段 | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `min_price` | float | 最低价格 | 从所有 SKU 价格计算 | | `max_price` | float | 最高价格 | 从所有 SKU 价格计算 | | `compare_at_price` | float | 原价/对比价 | MySQL: `shoplazza_product_spu.compare_at_price` | | `sku_prices` | float | 所有 SKU 价格列表(数组) | 从所有 SKU 价格汇总 | ### 8. 重量字段 | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `sku_weights` | long | 所有 SKU 重量列表(数组) | 从所有 SKU 重量汇总 | | `sku_weight_units` | keyword | 所有 SKU 重量单位列表(数组) | 从所有 SKU 重量单位汇总 | ### 9. 库存字段 | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `total_inventory` | long | 总库存(所有 SKU 库存之和) | 从所有 SKU 库存汇总 | ### 10. SKU 嵌套字段 | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `skus` | nested | SKU 详细信息列表(嵌套对象数组) | MySQL: `shoplazza_product_sku` | **嵌套结构**: ```json { "skus": [ { "sku_id": "sku_123", "price": 99.99, "compare_at_price": 149.99, "sku_code": "SKU001", "stock": 100, "weight": 0.5, "weight_unit": "kg", "option1_value": "white", "option2_value": "256GB", "option3_value": null, "image_src": "https://example.com/image.jpg" } ] } ``` **字段说明**: - `sku_id`: SKU 唯一标识 - `price`: SKU 价格 - `compare_at_price`: SKU 原价 - `sku_code`: SKU 编码 - `stock`: 库存数量 - `weight`: 重量 - `weight_unit`: 重量单位 - `option1_value`, `option2_value`, `option3_value`: 选项值(对应 `option1_name`, `option2_name`, `option3_name`) - `image_src`: SKU 图片地址(`index: false`,仅用于返回) ### 11. 图片字段 | 字段名 | ES类型 | 说明 | 数据来源 | |--------|--------|------|----------| | `image_url` | keyword | 主图URL(`index: false`,仅用于返回) | MySQL: `shoplazza_product_spu.image_url` | ### 12. 向量字段(不返回给前端) | 字段名 | ES类型 | 维度 | 说明 | 数据来源 | |--------|--------|------|------|----------| | `title_embedding` | dense_vector | 1024 | 标题向量(用于语义搜索) | 由 BGE-M3 模型生成 | | `image_embedding` | nested | - | 图片向量(用于图片搜索) | 由 CN-CLIP 模型生成 | **注意**: 这些字段仅用于搜索,不会返回给前端。 ## 字段用途总结 ### 搜索字段(参与相关性计算) - `title_zh`, `title_en` (boost: 3.0) - `brief_zh`, `brief_en` (boost: 1.5) - `description_zh`, `description_en` (boost: 1.0) - `vendor_zh`, `vendor_en` (boost: 1.5) - `tags` (boost: 1.0) - `category_path_zh`, `category_path_en` (boost: 1.5) - `category_name_zh`, `category_name_en` (boost: 1.5) - `title_embedding` (向量召回,boost: 0.2) ### 过滤字段(精确匹配) - `tenant_id` (必需,多租户隔离) - `category_id`, `category_name`, `category1_name`, `category2_name`, `category3_name` - `vendor_zh.keyword`, `vendor_en.keyword` - `specifications` (嵌套查询) - `min_price`, `max_price` (范围过滤) ### 分面字段(聚合统计) - `category1_name`, `category2_name`, `category3_name` - `specifications` (所有规格名称的分面,嵌套聚合,按 name 分组,然后按 value 聚合) - `specifications.{name}` (指定规格名称的分面,如 `specifications.color`,只返回该 name 的 value 列表) ### 返回字段(前端展示) 除 `title_embedding` 和 `image_embedding` 外,所有字段都会根据 `language` 参数自动选择对应的中英文字段返回。 ## 数据映射规则 ### 多语言字段映射 后端根据请求的 `language` 参数(`zh` 或 `en`)自动选择: - `language="zh"`: 优先返回 `*_zh` 字段,如果为空则回退到 `*_en` 字段 - `language="en"`: 优先返回 `*_en` 字段,如果为空则回退到 `*_zh` 字段 映射到前端字段: - `title_zh/en` → `title` - `brief_zh/en` → `brief` - `description_zh/en` → `description` - `vendor_zh/en` → `vendor` - `category_path_zh/en` → `category_path` - `category_name_zh/en` → `category_name` ### 规格数据构建 1. 从 `shoplazza_product_option` 表获取选项名称(`option1_name`, `option2_name`, `option3_name`) 2. 从 `shoplazza_product_sku` 表获取选项值(`option1`, `option2`, `option3`) 3. 将每个 SKU 的选项组合构建为 `specifications` 数组: ```python for sku in skus: if sku.option1 and option1_name: specifications.append({ "sku_id": sku.id, "name": option1_name, # 如"color" "value": sku.option1 # 如"white" }) # 同样处理 option2, option3 ``` ## 查询架构 ### 查询结构 ``` filters AND (text_recall OR embedding_recall) ``` - **filters**: 前端传递的过滤条件(永远起作用) - **text_recall**: 文本相关性召回(同时搜索中英文字段) - **embedding_recall**: 向量召回(KNN) - **function_score**: 包装召回部分,支持提权字段(新鲜度、销量等) ### 文本召回字段 默认同时搜索以下字段(中英文都包含): - `title_zh^3.0`, `title_en^3.0` - `brief_zh^1.5`, `brief_en^1.5` - `description_zh^1.0`, `description_en^1.0` - `vendor_zh^1.5`, `vendor_en^1.5` - `category_path_zh^1.5`, `category_path_en^1.5` - `category_name_zh^1.5`, `category_name_en^1.5` - `tags^1.0` ## 注意事项 1. **索引维度**: 所有数据以 SPU 为单位索引,SKU 信息作为嵌套字段存储 2. **多租户隔离**: 所有查询必须包含 `tenant_id` 过滤条件 3. **多语言支持**: 文本字段支持中英文,后端根据 `language` 参数自动选择 4. **规格分面**: `specifications` 使用嵌套聚合,按 `name` 分组,然后按 `value` 聚合 5. **向量字段**: `title_embedding` 和 `image_embedding` 仅用于搜索,不返回给前端