# 搜索API对接指南-01-搜索接口（POST /search/ 与响应）

本篇以 `POST /search/` 为主线，包含：
- 请求参数：`3.2`、过滤器：`3.3`、分面：`3.4`、SKU筛选维度：`3.5`
- 响应格式：第 `4` 章（4.1~4.5）
- 常见场景示例：第 `8` 章（示例整体并入本篇，避免散落）

## 搜索接口

### 3.1 接口信息

- **端点**: `POST /search/`
- **描述**: 执行文本搜索查询，支持多语言、过滤器和分面搜索
- **租户标识**：`tenant_id` 通过 HTTP 请求头 **`X-Tenant-ID`** 传递（推荐）；也可通过 URL query 参数 **`tenant_id`** 传递。**不要放在请求体中。**

**请求示例（推荐）**:

```python
url = f"{base_url.rstrip('/')}/search/"
headers = {
    "Content-Type": "application/json",
    "X-Tenant-ID": "162",  # 租户ID，必填
}
response = requests.post(url, headers=headers, json={"query": "芭比娃娃"})
```

**cURL 示例（复制即可试）**

将 `BASE_URL`、`TENANT_ID` 换成你的环境与租户。本地开发默认后端为 `http://localhost:6002`（见 `scripts/start_backend.sh`）。

```bash
export BASE_URL="${BASE_URL:-http://localhost:6002}"
export TENANT_ID="${TENANT_ID:-162}"   # 改成真实租户
```

**1）基础检索**：关键词、分页、`language` 控制返回字段语言。

```bash
curl -sS "$BASE_URL/search/" \
  -H "Content-Type: application/json" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -d '{
    "query": "芭比娃娃",
    "size": 20,
    "from": 0,
    "language": "zh"
  }'
```

**2）过滤 + 价格区间 + 分面 + 排序**：联调最常见组合（品牌 OR、`min_price` 区间、类目与颜色分面、按最低价升序）。

```bash
curl -sS "$BASE_URL/search/" \
  -H "Content-Type: application/json" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -d '{
    "query": "手机",
    "size": 20,
    "from": 0,
    "language": "zh",
    "filters": {
      "vendor.zh.keyword": ["品牌A", "品牌B"]
    },
    "range_filters": {
      "min_price": {"gte": 50, "lte": 200}
    },
    "facets": [
      {"field": "category1_name", "size": 15, "type": "terms"},
      {"field": "specifications.color", "size": 20, "type": "terms"}
    ],
    "sort_by": "min_price",
    "sort_order": "asc"
  }'
```

**3）规格过滤 + SKU 维度 + 调试**：`specifications` 嵌套过滤、`sku_filter_dimension` 每组只保留一个 SKU；`debug: true` 便于对照 ES 与解析结果。

```bash
curl -sS "$BASE_URL/search/" \
  -H "Content-Type: application/json" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -d '{
    "query": "手机",
    "size": 10,
    "from": 0,
    "language": "zh",
    "filters": {
      "specifications": {"name": "color", "value": "white"}
    },
    "sku_filter_dimension": ["color"],
    "debug": true
  }'
```

需要美化 JSON 输出时，在命令末尾追加 ` | jq .`（需本机安装 [jq](https://jqlang.github.io/jq/)）。

### 3.2 请求参数

#### 完整请求体结构

```json
{
  "query": "string (required)",
  "size": 10,
  "from": 0,
  "language": "zh",
  "filters": {},
  "range_filters": {},
  "facets": [],
  "sort_by": "string",
  "sort_order": "desc",
  "min_score": 0.0,
  "sku_filter_dimension": ["string"],
  "debug": false,
  "enable_rerank": null,
  "rerank_query_template": "{query}",
  "rerank_doc_template": "{title}",
  "user_id": "string",
  "session_id": "string"
}
```

#### 参数详细说明

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `query` | string | Y | - | 搜索查询字符串（统一文本检索策略） |
| `size` | integer | N | 10 | 返回结果数量（1-100） |
| `from` | integer | N | 0 | 分页偏移量（用于分页） |
| `language` | string | N | "zh" | 返回语言：`zh`（中文）或 `en`（英文）。后端会根据此参数选择对应的中英文字段返回 |
| `filters` | object | N | null | 精确匹配过滤器（见[过滤器详解](#33-过滤器详解)） |
| `range_filters` | object | N | null | 数值范围过滤器（见[过滤器详解](#33-过滤器详解)） |
| `facets` | array | N | null | 分面配置（见[分面配置](#34-分面配置)） |
| `sort_by` | string | N | null | 排序字段名。支持：`price`（价格）、`sales`（销量）、`create_time`（创建时间）、`update_time`（更新时间）。默认按相关性排序 |
| `sort_order` | string | N | "desc" | 排序方向：`asc`（升序）或 `desc`（降序）。注意：`price`+`asc`=价格从低到高，`price`+`desc`=价格从高到低（后端自动映射为min_price或max_price） |
| `min_score` | float | N | null | 最小相关性分数阈值 |
| `sku_filter_dimension` | array[string] | N | null | 子SKU筛选维度列表（见[SKU筛选维度](#35-sku筛选维度)） |
| `debug` | boolean | N | false | 是否返回调试信息 |
| `enable_rerank` | boolean/null | N | null | 是否开启重排（调用外部重排服务对 ES 结果进行二次排序）。不传/传 null 使用服务端 `rerank.enabled`。当有效开启且 `from + size <= rerank_window` 时：ES 先取前 `rerank_window` 条，重排后再按 `from`/`size` 截取当前页；若 `from + size > rerank_window`，则**不进行**窗口内重排，直接按请求的 `from`/`size` 查询 ES（`rerank_window` 见 `config.yaml` 的 `rerank.rerank_window`，仓库示例默认 400） |
| `rerank_query_template` | string | N | null | 重排 query 模板（可选）。支持 `{query}` 占位符；不传则使用服务端 `rerank.rerank_query_template` |
| `rerank_doc_template` | string | N | null | 重排 doc 模板（可选）。支持 `{title} {brief} {vendor} {description} {category_path}` 等占位符（由 `search/rerank_client.py` 按语言字段拼装）；不传则使用服务端 `rerank.rerank_doc_template` |

**与后端代码的对应关系**（便于联调）：HTTP `POST /search/` 请求体由 `api/models.py` 的 `SearchRequest` 校验；路由 `api/routes/search.py` 将字段原样传入 `Searcher.search(...)`（含上述三个重排相关字段）。CLI `python main.py search` 目前未暴露这些参数，走配置默认值。
| `user_id` | string | N | null | 用户ID（用于个性化，预留） |
| `session_id` | string | N | null | 会话ID（用于分析，预留） |

### 3.3 过滤器详解

#### 3.3.1 精确匹配过滤器 (filters)

用于精确匹配或多值匹配。对于普通字段，数组表示 OR 逻辑（匹配任意一个值）；对于 specifications 字段，按维度分组处理。**任意字段名加 `_all` 后缀**表示多值 AND 逻辑（必须同时匹配所有值）。

**格式**:

```json
{
  "filters": {
    "category_name": "手机",                      // 可以为单值 或者 数组 匹配数组中任意一个（OR）
    "category1_name": "服装",                    // 可以为单值 或者 数组 匹配数组中任意一个（OR）
    "category2_name": "男装",                    // 可以为单值 或者 数组 匹配数组中任意一个（OR）
    "category3_name": "衬衫",                    // 可以为单值 或者 数组 匹配数组中任意一个（OR）
    "vendor.zh.keyword": ["奇乐", "品牌A"],      // 可以为单值 或者 数组 匹配数组中任意一个（OR）
    "tags": "手机",                              // 可以为单值 或者 数组 匹配数组中任意一个（OR）
    "tags_all": ["手机", "促销", "新品"],        // *_all：多值为 AND，必须同时包含所有标签
    "category1_name_all": ["服装", "男装"],     // 同上，适用于任意可过滤字段
    // specifications 嵌套过滤（特殊格式）
    "specifications": {
      "name": "color",
      "value": "white"
    }
  }
}
```

**支持的值类型**:
- 字符串：精确匹配
- 整数：精确匹配
- 布尔值：精确匹配
- 数组：匹配任意值（OR 逻辑）；若字段名以 `_all` 结尾，则数组表示 AND 逻辑（必须同时匹配所有值）
- 对象：specifications 嵌套过滤（见下文）

**`*_all` 语义（多值 AND）**:
- 任意过滤字段均可使用 `_all` 后缀，对应 ES 字段名为去掉 `_all` 后的名称。
- 例如：`tags_all: ["A", "B"]` 表示文档的 `tags` 必须**同时包含** A 和 B；`vendor.zh.keyword_all: ["奇乐", "品牌A"]` 表示同时匹配两个品牌（通常用于 keyword 多值场景）。
- `specifications_all`：传列表 `[{"name":"color","value":"white"},{"name":"size","value":"256GB"}]` 时，表示所有列出的规格条件都要满足（与 `specifications` 多维度时的 AND 一致；若同维度多值则要求文档同时满足多个值，一般用于嵌套多值场景）。

**Specifications 嵌套过滤**:

`specifications` 是嵌套字段，支持按规格名称和值进行过滤。

**单个规格过滤**:

```json
{
  "filters": {
    "specifications": {
      "name": "color",
      "value": "white"
    }
  }
}
```

查询规格名称为"color"且值为"white"的商品。

**多个规格过滤（按维度分组）**:

```json
{
  "filters": {
    "specifications": [
      {"name": "color", "value": "white"},
      {"name": "size", "value": "256GB"}
    ]
  }
}
```

查询同时满足所有规格的商品（color=white **且** size=256GB）。

**相同维度的多个值（OR 逻辑）**:

```json
{
  "filters": {
    "specifications": [
      {"name": "size", "value": "3"},
      {"name": "size", "value": "4"},
      {"name": "size", "value": "5"},
      {"name": "color", "value": "green"}
    ]
  }
}
```

查询满足 (size=3 **或** size=4 **或** size=5) **且** color=green 的商品。

**过滤逻辑说明**:
- **不同维度**（不同的 `name`）之间是 **AND** 关系（求交集）
- **相同维度**（相同的 `name`）的多个值之间是 **OR** 关系（求并集）

**常用过滤字段**（详见[常用字段列表](./搜索API对接指南-08-数据模型与字段速查.md#93-常用字段列表)）:
- `category_name`: 类目名称
- `category1_name`, `category2_name`, `category3_name`: 多级类目
- `category_id`: 类目ID
- `vendor.zh.keyword`, `vendor.en.keyword`: 供应商/品牌（使用keyword子字段）
- `tags`: 标签（keyword类型，支持数组）
- `option1_name`, `option2_name`, `option3_name`: 选项名称
- `specifications`: 规格过滤（嵌套字段，格式见上文）
- 以上任意字段均可加 `_all` 后缀表示多值 AND，如 `tags_all`、`category1_name_all`。

#### 3.3.2 范围过滤器 (range_filters)

用于数值字段的范围过滤。

**格式**:

```json
{
  "range_filters": {
    "min_price": {
      "gte": 50,    // 大于等于
      "lte": 200    // 小于等于
    },
    "max_price": {
      "gt": 100     // 大于
    },
    "create_time": {
      "gte": "2024-01-01T00:00:00Z"  // 日期时间字符串
    }
  }
}
```

**支持的操作符**:
- `gte`: 大于等于 (>=)
- `gt`: 大于 (>)
- `lte`: 小于等于 (<=)
- `lt`: 小于 (<)

**注意**: 至少需要指定一个操作符。

**常用范围字段**（详见[常用字段列表](./搜索API对接指南-08-数据模型与字段速查.md#93-常用字段列表)）:
- `min_price`: 最低价格
- `max_price`: 最高价格
- `compare_at_price`: 原价
- `create_time`: 创建时间
- `update_time`: 更新时间

### 3.4 分面配置

用于生成分面统计（分组聚合），常用于构建筛选器UI。

#### 3.4.1 配置格式

```json
{
  "facets": [
    {
      "field": "category1_name",
      "size": 15,
      "type": "terms",
      "disjunctive": false
    },
    {
      "field": "brand_name",
      "size": 10,
      "type": "terms",
      "disjunctive": true
    },
    {
      "field": "specifications.color",
      "size": 20,
      "type": "terms",
      "disjunctive": true
    },
    {
      "field": "min_price",
      "type": "range",
      "ranges": [
        {"key": "0-50", "to": 50},
        {"key": "50-100", "from": 50, "to": 100},
        {"key": "100-200", "from": 100, "to": 200},
        {"key": "200+", "from": 200}
      ]
    }
  ]
}
```

#### 3.4.2 Facet 字段说明

| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `field` | string | 是 | - | 分面字段名 |
| `size` | int | 否 | 10 | 返回的分面值数量（1-100） |
| `type` | string | 否 | "terms" | 分面类型：`terms`（词条聚合）或 `range`（范围聚合） |
| `disjunctive` | bool | 否 | false | 是否支持多选（disjunctive faceting）。启用后，选中该分面的过滤器时，仍会显示其他可选项 |
| `ranges` | array | 否 | null | 范围配置（仅 `type="range"` 时需要） |

#### 3.4.3 disjunctive字段说明

**重要特性**: `disjunctive` 字段控制分面的行为模式。启用后，选中该分面的过滤器时，仍会显示其他可选项

**标准模式 (disjunctive: false)**:
- **行为**: 选中某个分面值后，该分面只显示选中的值
- **适用场景**: 层级类目、互斥选择
- **示例**: 类目下钻（玩具 > 娃娃 > 芭比）

**Multi-Select 模式 (disjunctive: true)** ⭐:
- **行为**: 选中某个分面值后，该分面仍显示所有可选项
- **适用场景**: 颜色、品牌、尺码等可切换属性
- **示例**: 选择了"红色"后，仍能看到"蓝色"、"绿色"等选项

**推荐配置**:

| 分面类型 | disjunctive | 原因 |
|---------|-------------|------|
| 颜色 | `true` | 用户需要切换颜色 |
| 品牌 | `true` | 用户需要比较品牌 |
| 尺码 | `true` | 用户需要查看其他尺码 |
| 类目 | `false` | 层级下钻 |
| 价格区间 | `false` | 互斥选择 |

#### 3.4.4 规格分面说明

`specifications` 是嵌套字段，支持两种分面模式：

**模式1：所有规格名称的分面**:

```json
{
  "facets": [
    {
      "field": "specifications",
      "size": 10,
      "type": "terms"
    }
  ]
}
```

返回所有规格名称（name）及其对应的值（value）列表。每个 name 会生成一个独立的分面结果。

**模式2：指定规格名称的分面**:

```json
{
  "facets": [
    {
      "field": "specifications.color",
      "size": 20,
      "type": "terms",
      "disjunctive": true
    },
    {
      "field": "specifications.size",
      "size": 15,
      "type": "terms",
      "disjunctive": true
    }
  ]
}
```

只返回指定规格名称的值列表。格式：`specifications.{name}`，其中 `{name}` 是规格名称（如"color"、"size"、"material"）。

**返回格式示例**:

```json
{
  "facets": [
    {
      "field": "specifications.color",
      "label": "color",
      "type": "terms",
      "values": [
        {"value": "white", "count": 50, "selected": true},  // ✓ selected 字段由后端标记
        {"value": "black", "count": 30, "selected": false},
        {"value": "red", "count": 20, "selected": false}
      ]
    },
    {
      "field": "specifications.size",
      "label": "size",
      "type": "terms",
      "values": [
        {"value": "256GB", "count": 40, "selected": false},
        {"value": "512GB", "count": 20, "selected": false}
      ]
    }
  ]
}
```

### 3.5 SKU筛选维度

**功能说明**:
`sku_filter_dimension` 用于控制搜索列表页中 **每个 SPU 下方可切换的子款式（子 SKU）维度**，为字符串列表。  
在店铺的 **主题装修配置** 中，商家可以为店铺设置一个或多个子款式筛选维度（例如 `color`、`size`），前端列表页会在每个 SPU 下展示这些维度对应的子 SKU 列表，用户可以通过点击不同维度值（如不同颜色）来切换展示的子款式。  
当指定 `sku_filter_dimension` 后，后端会根据店铺的这项配置，从所有 SKU 中筛选出这些维度组合对应的子 SKU 数据：系统会按指定维度**组合**对 SKU 进行分组，每个维度组合只返回第一个 SKU（从简实现，选择该组合下的第一款），其余不在这些维度组合中的子 SKU 将不返回。

**支持的维度值**:
1. **直接选项字段**: `option1`、`option2`、`option3`
   - 直接使用对应的 `option1_value`、`option2_value`、`option3_value` 字段进行分组
   
2. **规格/选项名称**: 通过 `option1_name`、`option2_name`、`option3_name` 匹配
   - 例如：如果 `option1_name` 为 `"color"`，则可以使用 `sku_filter_dimension: ["color"]` 来按颜色分组

**示例**:

**按颜色筛选（假设 option1_name = "color"）**:

```json
{
  "query": "芭比娃娃",
  "sku_filter_dimension": ["color"]
}
```

**按选项1筛选**:

```json
{
  "query": "芭比娃娃",
  "sku_filter_dimension": ["option1"]
}
```

**按颜色 + 尺寸组合筛选（假设 option1_name = "color", option2_name = "size"）**:

```json
{
  "query": "芭比娃娃",
  "sku_filter_dimension": ["color", "size"]
}
```

## 响应格式说明

### 4.1 标准响应结构

```json
{
  "results": [
    {
      "spu_id": "12345",
      "title": "芭比时尚娃娃",
      "brief": "高品质芭比娃娃",
      "description": "详细描述...",
      "vendor": "美泰",
      "category": "玩具",
      "category_path": "玩具/娃娃/时尚",
      "category_name": "时尚",
      "category_id": "cat_001",
      "category_level": 3,
      "category1_name": "玩具",
      "category2_name": "娃娃",
      "category3_name": "时尚",
      "tags": ["娃娃", "玩具", "女孩"],
      "price": 89.99,
      "compare_at_price": 129.99,
      "currency": "USD",
      "image_url": "https://example.com/image.jpg",
      "in_stock": true,
      "sku_prices": [89.99, 99.99, 109.99],
      "sku_weights": [100, 150, 200],
      "sku_weight_units": ["g", "g", "g"],
      "total_inventory": 500,
      "option1_name": "color",
      "option2_name": "size",
      "option3_name": null,
      "specifications": [
        {"sku_id": "sku_001", "name": "color", "value": "pink"},
        {"sku_id": "sku_001", "name": "size", "value": "standard"}
      ],
      "skus": [
        {
          "sku_id": "67890",
          "price": 89.99,
          "compare_at_price": 129.99,
          "sku": "BARBIE-001",
          "stock": 100,
          "weight": 0.1,
          "weight_unit": "kg",
          "option1_value": "pink",
          "option2_value": "standard",
          "option3_value": null,
          "image_src": "https://example.com/sku1.jpg"
        }
      ],
      "relevance_score": 8.5
    }
  ],
  "total": 118,
  "max_score": 8.5,
  "facets": [
    {
      "field": "category1_name",
      "label": "category1_name",
      "type": "terms",
      "values": [
        {
          "value": "玩具",
          "label": "玩具",
          "count": 85,
          "selected": false
        }
      ]
    },
    {
      "field": "specifications.color",
      "label": "color",
      "type": "terms",
      "values": [
        {
          "value": "pink",
          "label": "pink",
          "count": 30,
          "selected": false
        }
      ]
    }
  ],
  "query_info": {
    "original_query": "芭比娃娃",
    "query_normalized": "芭比娃娃",
    "rewritten_query": "芭比娃娃",
    "detected_language": "zh",
    "translations": {
      "en": "barbie doll"
    },
    "domain": "default"
  },
  "suggestions": [],
  "related_searches": [],
  "took_ms": 45,
  "performance_info": null,
  "debug_info": null
}
```

### 4.2 响应字段说明

| 字段 | 类型 | 说明 |
|------|------|------|
| `results` | array | 搜索结果列表（SpuResult对象数组） |
| `results[].spu_id` | string | SPU ID |
| `results[].title` | string | 商品标题 |
| `results[].price` | float | 价格（min_price） |
| `results[].skus` | array | SKU列表（如果指定了`sku_filter_dimension`，则按维度过滤后的SKU） |
| `results[].relevance_score` | float | 相关性分数 |
| `total` | integer | 匹配的总文档数 |
| `max_score` | float | 最高相关性分数 |
| `facets` | array | 分面统计结果 |
| `query_info` | object | query处理信息 |
| `took_ms` | integer | 搜索耗时（毫秒） |
| `debug_info` | object/null | 调试信息，仅当请求传 `debug=true` 时返回 |

#### 4.2.1 query_info 说明

`query_info` 包含本次搜索的查询解析与处理结果：

| 子字段 | 类型 | 说明 |
|--------|------|------|
| `original_query` | string | 用户原始查询 |
| `query_normalized` | string | 归一化后的查询（去空白、大小写等预处理，用于后续解析与改写） |
| `rewritten_query` | string | 重写后的查询（同义词/词典扩展等） |
| `detected_language` | string | 检测到的查询语言（如 `zh`、`en`） |
| `translations` | object | 翻译结果，键为语言代码，值为翻译文本 |
| `domain` | string | 查询域（如 `default`、`title`、`brand` 等） |

#### 4.2.2 debug_info 说明

`debug_info` 主要用于检索效果评估、融合打分分析与 bad case 排查。

`debug_info.query_analysis` 常见字段：

| 子字段 | 类型 | 说明 |
|--------|------|------|
| `original_query` | string | 原始查询 |
| `query_normalized` | string | 归一化后的查询 |
| `rewritten_query` | string | 重写后的查询 |
| `detected_language` | string | 检测到的语言 |
| `translations` | object | 翻译结果 |
| `has_vector` | boolean | 是否生成了向量 |

`debug_info.per_result[]` 常见字段：

| 子字段 | 类型 | 说明 |
|--------|------|------|
| `spu_id` | string | 结果 SPU ID |
| `es_score` | float | ES 原始 `_score` |
| `rerank_score` | float | 重排分数 |
| `text_score` | float | 文本相关性大分（由 `base_query` / `base_query_trans_*` 聚合而来） |
| `text_source_score` | float | `base_query` 分数 |
| `text_translation_score` | float | `base_query_trans_*` 里的最大分数 |
| `text_primary_score` | float | 文本大分中的主证据部分 |
| `text_support_score` | float | 文本大分中的辅助证据部分 |
| `knn_score` | float | `knn_query` 分数 |
| `fused_score` | float | 最终融合分数 |
| `matched_queries` | object/array | ES named queries 命中详情 |

### 4.3 SpuResult字段说明

| 字段 | 类型 | 说明 |
|------|------|------|
| `spu_id` | string | SPU ID |
| `title` | string | 商品标题（根据language参数自动选择 `title.zh` 或 `title.en`） |
| `brief` | string | 商品短描述（根据language参数自动选择） |
| `description` | string | 商品详细描述（根据language参数自动选择） |
| `vendor` | string | 供应商/品牌（根据language参数自动选择） |
| `category` | string | 类目（兼容字段，等同于category_name） |
| `category_path` | string | 类目路径（多级，用于面包屑，根据language参数自动选择） |
| `category_name` | string | 类目名称（展示用，根据language参数自动选择） |
| `category_id` | string | 类目ID |
| `category_level` | integer | 类目层级（1/2/3） |
| `category1_name` | string | 一级类目名称 |
| `category2_name` | string | 二级类目名称 |
| `category3_name` | string | 三级类目名称 |
| `tags` | array[string] | 标签列表 |
| `price` | float | 价格（min_price） |
| `compare_at_price` | float | 原价 |
| `currency` | string | 货币单位（默认USD） |
| `image_url` | string | 主图URL |
| `in_stock` | boolean | 是否有库存（任意SKU有库存即为true） |
| `sku_prices` | array[float] | 所有SKU价格列表 |
| `sku_weights` | array[integer] | 所有SKU重量列表 |
| `sku_weight_units` | array[string] | 所有SKU重量单位列表 |
| `total_inventory` | integer | 总库存 |
| `sales` | integer | 销量（展示销量） |
| `option1_name` | string | 选项1名称（如"color"） |
| `option2_name` | string | 选项2名称（如"size"） |
| `option3_name` | string | 选项3名称 |
| `specifications` | array[object] | 规格列表（与ES specifications字段对应） |
| `skus` | array | SKU 列表 |
| `relevance_score` | float | 相关性分数（默认为 ES 原始分数；当开启 AI 搜索时为融合后的最终分数） |

### 4.4 SkuResult字段说明

| 字段 | 类型 | 说明 |
|------|------|------|
| `sku_id` | string | SKU ID |
| `price` | float | 价格 |
| `compare_at_price` | float | 原价 |
| `sku` | string | SKU编码（sku_code） |
| `stock` | integer | 库存数量 |
| `weight` | float | 重量 |
| `weight_unit` | string | 重量单位 |
| `option1_value` | string | 选项1取值（如color值） |
| `option2_value` | string | 选项2取值（如size值） |
| `option3_value` | string | 选项3取值 |
| `image_src` | string | SKU图片地址 |

### 4.5 多语言字段说明

- `title`, `brief`, `description`, `vendor`, `category_path`, `category_name` 会根据请求的 `language` 参数自动选择对应的中英文字段
- `language="zh"`: 优先返回 `*_zh` 字段，如果为空则回退到 `*_en` 字段
- `language="en"`: 优先返回 `*_en` 字段，如果为空则回退到 `*_zh` 字段

---

## 8. 常见场景示例

以下示例仅展示**请求体**（body）；实际调用时请加上请求头 `X-Tenant-ID: <租户ID>`（或 URL 参数 `tenant_id`），参见 [3.1 接口信息](#31-接口信息)。

### 8.1 基础搜索与排序

**按价格从低到高排序**:

```json
{
  "query": "玩具",
  "size": 20,
  "from": 0,
  "sort_by": "price",
  "sort_order": "asc"
}
```

**按价格从高到低排序**:

```json
{
  "query": "玩具",
  "size": 20,
  "from": 0,
  "sort_by": "price",
  "sort_order": "desc"
}
```

**按销量从高到低排序**:

```json
{
  "query": "玩具",
  "size": 20,
  "from": 0,
  "sort_by": "sales",
  "sort_order": "desc"
}
```

**按默认（相关性）排序**:

```json
{
  "query": "玩具",
  "size": 20,
  "from": 0
}
```

### 8.2 过滤搜索

**需求**: 搜索"玩具"，筛选类目为"益智玩具"，价格在50-200之间

```json
{
  "query": "玩具",
  "size": 20,
  "language": "zh",
  "filters": {
    "category_name": "益智玩具"
  },
  "range_filters": {
    "min_price": {
      "gte": 50,
      "lte": 200
    }
  }
}
```

**需求**: 搜索"手机"，筛选多个品牌，价格范围

```json
{
  "query": "手机",
  "size": 20,
  "language": "zh",
  "filters": {
    "vendor.zh.keyword": ["品牌A", "品牌B"]
  },
  "range_filters": {
    "min_price": {
      "gte": 50,
      "lte": 200
    }
  }
}
```

### 8.3 分面搜索

**需求**: 搜索"玩具"，获取类目和规格的分面统计，用于构建筛选器

```json
{
  "query": "玩具",
  "size": 20,
  "language": "zh",
  "facets": [
    {"field": "category1_name", "size": 15, "type": "terms"},
    {"field": "category2_name", "size": 10, "type": "terms"},
    {"field": "specifications", "size": 10, "type": "terms"}
  ]
}
```

**需求**: 搜索"手机"，获取价格区间和规格的分面统计

```json
{
  "query": "手机",
  "size": 20,
  "language": "zh",
  "facets": [
    {
      "field": "min_price",
      "type": "range",
      "ranges": [
        {"key": "0-50", "to": 50},
        {"key": "50-100", "from": 50, "to": 100},
        {"key": "100-200", "from": 100, "to": 200},
        {"key": "200+", "from": 200}
      ]
    },
    {
      "field": "specifications",
      "size": 10,
      "type": "terms"
    }
  ]
}
```

### 8.4 规格过滤与分面

**需求**: 搜索"手机"，筛选color为"white"的商品

```json
{
  "query": "手机",
  "size": 20,
  "language": "zh",
  "filters": {
    "specifications": {
      "name": "color",
      "value": "white"
    }
  }
}
```

**需求**: 搜索"手机"，筛选color为"white"且size为"256GB"的商品

```json
{
  "query": "手机",
  "size": 20,
  "language": "zh",
  "filters": {
    "specifications": [
      {"name": "color", "value": "white"},
      {"name": "size", "value": "256GB"}
    ]
  }
}
```

**需求**: 搜索"手机"，筛选size为"3"、"4"或"5"，且color为"green"的商品

```json
{
  "query": "手机",
  "size": 20,
  "language": "zh",
  "filters": {
    "specifications": [
      {"name": "size", "value": "3"},
      {"name": "size", "value": "4"},
      {"name": "size", "value": "5"},
      {"name": "color", "value": "green"}
    ]
  }
}
```

**需求**: 搜索"手机"，获取所有规格的分面统计

```json
{
  "query": "手机",
  "size": 20,
  "language": "zh",
  "facets": [
    {"field": "specifications", "size": 10, "type": "terms"}
  ]
}
```

**需求**: 只获取"color"和"size"规格的分面统计

```json
{
  "query": "手机",
  "size": 20,
  "language": "zh",
  "facets": [
    {"field": "specifications.color", "size": 20, "type": "terms"},
    {"field": "specifications.size", "size": 15, "type": "terms"}
  ]
}
```

**需求**: 搜索"手机"，筛选类目和规格，并获取对应的分面统计

```json
{
  "query": "手机",
  "size": 20,
  "language": "zh",
  "filters": {
    "category_name": "手机",
    "specifications": {
      "name": "color",
      "value": "white"
    }
  },
  "facets": [
    {"field": "category1_name", "size": 15, "type": "terms"},
    {"field": "category2_name", "size": 10, "type": "terms"},
    {"field": "specifications.color", "size": 20, "type": "terms"},
    {"field": "specifications.size", "size": 15, "type": "terms"}
  ]
}
```

### 8.5 SKU筛选

**需求**: 搜索"芭比娃娃"，每个SPU下按颜色筛选，每种颜色只显示一个SKU

```json
{
  "query": "芭比娃娃",
  "size": 20,
  "sku_filter_dimension": ["color"]
}
```

**说明**:
- 如果 `option1_name` 为 `"color"`，则使用 `sku_filter_dimension: ["color"]` 可以按颜色分组
- 每个SPU下，每种颜色只会返回第一个SKU
- 如果维度不匹配，返回所有SKU（不进行过滤）

### 8.6 分页查询

**需求**: 获取第2页结果（每页20条）

```json
{
  "query": "手机",
  "size": 20,
  "from": 20
}
```

**cURL（第 2 页，每页 20 条）**：

```bash
curl -sS "${BASE_URL:-http://localhost:6002}/search/" \
  -H "Content-Type: application/json" \
  -H "X-Tenant-ID: ${TENANT_ID:-162}" \
  -d '{
    "query": "手机",
    "size": 20,
    "from": 20,
    "language": "zh"
  }'
```

---

