Commit b73baf85cb520d7316d2514d24edfec5e8334b7a

Authored by tangwang
1 parent cd3799c6

撰写接口文档

API_INTEGRATION_GUIDE.md 0 → 100644
@@ -0,0 +1,791 @@ @@ -0,0 +1,791 @@
  1 +# 搜索API接口对接指南
  2 +
  3 +本文档为搜索服务的使用方提供完整的API对接指南,包括接口说明、请求参数、响应格式和使用示例。
  4 +
  5 +## 目录
  6 +
  7 +1. [快速开始](#快速开始)
  8 +2. [接口概览](#接口概览)
  9 +3. [文本搜索接口](#文本搜索接口)
  10 +4. [图片搜索接口](#图片搜索接口)
  11 +5. [响应格式说明](#响应格式说明)
  12 +6. [常见场景示例](#常见场景示例)
  13 +7. [错误处理](#错误处理)
  14 +8. [最佳实践](#最佳实践)
  15 +
  16 +---
  17 +
  18 +## 快速开始
  19 +
  20 +### 基础信息
  21 +
  22 +- **Base URL**: `http://your-domain:6002` 或 `http://120.76.41.98:6002`
  23 +- **协议**: HTTP/HTTPS
  24 +- **数据格式**: JSON
  25 +- **字符编码**: UTF-8
  26 +- **请求方法**: POST(搜索接口)
  27 +
  28 +### 最简单的搜索请求
  29 +
  30 +```bash
  31 +curl -X POST "http://localhost:6002/search/" \
  32 + -H "Content-Type: application/json" \
  33 + -d '{
  34 + "query": "芭比娃娃"
  35 + }'
  36 +```
  37 +
  38 +### Python示例
  39 +
  40 +```python
  41 +import requests
  42 +
  43 +url = "http://localhost:6002/search/"
  44 +response = requests.post(url, json={"query": "芭比娃娃"})
  45 +data = response.json()
  46 +print(f"找到 {data['total']} 个结果")
  47 +```
  48 +
  49 +### JavaScript示例
  50 +
  51 +```javascript
  52 +const response = await fetch('http://localhost:6002/search/', {
  53 + method: 'POST',
  54 + headers: {
  55 + 'Content-Type': 'application/json'
  56 + },
  57 + body: JSON.stringify({
  58 + query: '芭比娃娃'
  59 + })
  60 +});
  61 +const data = await response.json();
  62 +console.log(`找到 ${data.total} 个结果`);
  63 +```
  64 +
  65 +---
  66 +
  67 +## 接口概览
  68 +
  69 +| 接口 | 方法 | 路径 | 说明 |
  70 +|------|------|------|------|
  71 +| 文本搜索 | POST | `/search/` | 执行文本搜索查询 |
  72 +| 图片搜索 | POST | `/search/image` | 基于图片相似度搜索 |
  73 +| 搜索建议 | GET | `/search/suggestions` | 获取搜索建议(框架,暂未实现) |
  74 +| 获取文档 | GET | `/search/{doc_id}` | 根据ID获取单个文档 |
  75 +| 健康检查 | GET | `/admin/health` | 检查服务状态 |
  76 +
  77 +---
  78 +
  79 +## 文本搜索接口
  80 +
  81 +### 接口信息
  82 +
  83 +- **端点**: `POST /search/`
  84 +- **描述**: 执行文本搜索查询,支持多语言、布尔表达式、过滤器和分面搜索
  85 +
  86 +### 请求参数
  87 +
  88 +#### 完整请求体结构
  89 +
  90 +```json
  91 +{
  92 + "query": "string (required)",
  93 + "size": 10,
  94 + "from": 0,
  95 + "filters": {},
  96 + "range_filters": {},
  97 + "facets": [],
  98 + "sort_by": "string",
  99 + "sort_order": "desc",
  100 + "min_score": 0.0,
  101 + "debug": false,
  102 + "user_id": "string",
  103 + "session_id": "string"
  104 +}
  105 +```
  106 +
  107 +#### 参数详细说明
  108 +
  109 +| 参数 | 类型 | 必填 | 默认值 | 说明 |
  110 +|------|------|------|--------|------|
  111 +| `query` | string | ✅ | - | 搜索查询字符串,支持布尔表达式(AND, OR, RANK, ANDNOT) |
  112 +| `size` | integer | ❌ | 10 | 返回结果数量(1-100) |
  113 +| `from` | integer | ❌ | 0 | 分页偏移量(用于分页) |
  114 +| `filters` | object | ❌ | null | 精确匹配过滤器(见下文) |
  115 +| `range_filters` | object | ❌ | null | 数值范围过滤器(见下文) |
  116 +| `facets` | array | ❌ | null | 分面配置(见下文) |
  117 +| `sort_by` | string | ❌ | null | 排序字段名(如 `min_price`, `max_price`, `title`) |
  118 +| `sort_order` | string | ❌ | "desc" | 排序方向:`asc`(升序)或 `desc`(降序) |
  119 +| `min_score` | float | ❌ | null | 最小相关性分数阈值 |
  120 +| `debug` | boolean | ❌ | false | 是否返回调试信息 |
  121 +| `user_id` | string | ❌ | null | 用户ID(用于个性化,预留) |
  122 +| `session_id` | string | ❌ | null | 会话ID(用于分析,预留) |
  123 +
  124 +### 过滤器详解
  125 +
  126 +#### 1. 精确匹配过滤器 (filters)
  127 +
  128 +用于精确匹配或多值匹配(OR 逻辑)。
  129 +
  130 +**格式**:
  131 +```json
  132 +{
  133 + "filters": {
  134 + "category_keyword": "玩具", // 单值:精确匹配
  135 + "vendor_keyword": ["乐高", "孩之宝"], // 数组:匹配任意值(OR)
  136 + "product_type_keyword": "益智玩具" // 单值:精确匹配
  137 + }
  138 +}
  139 +```
  140 +
  141 +**支持的值类型**:
  142 +- 字符串:精确匹配
  143 +- 整数:精确匹配
  144 +- 布尔值:精确匹配
  145 +- 数组:匹配任意值(OR 逻辑)
  146 +
  147 +**常用过滤字段**:
  148 +- `category_keyword`: 类目
  149 +- `vendor_keyword`: 品牌/供应商
  150 +- `product_type_keyword`: 商品类型
  151 +- `tags_keyword`: 标签
  152 +
  153 +#### 2. 范围过滤器 (range_filters)
  154 +
  155 +用于数值字段的范围过滤。
  156 +
  157 +**格式**:
  158 +```json
  159 +{
  160 + "range_filters": {
  161 + "min_price": {
  162 + "gte": 50, // 大于等于
  163 + "lte": 200 // 小于等于
  164 + },
  165 + "max_price": {
  166 + "gt": 100 // 大于
  167 + },
  168 + "create_time": {
  169 + "gte": "2024-01-01T00:00:00Z" // 日期时间字符串
  170 + }
  171 + }
  172 +}
  173 +```
  174 +
  175 +**支持的操作符**:
  176 +- `gte`: 大于等于 (>=)
  177 +- `gt`: 大于 (>)
  178 +- `lte`: 小于等于 (<=)
  179 +- `lt`: 小于 (<)
  180 +
  181 +**注意**: 至少需要指定一个操作符。
  182 +
  183 +**常用范围字段**:
  184 +- `min_price`: 最低价格
  185 +- `max_price`: 最高价格
  186 +- `compare_at_price`: 原价
  187 +- `create_time`: 创建时间
  188 +- `update_time`: 更新时间
  189 +
  190 +#### 3. 分面配置 (facets)
  191 +
  192 +用于生成分面统计(分组聚合),常用于构建筛选器UI。
  193 +
  194 +**简单模式**(字符串数组):
  195 +```json
  196 +{
  197 + "facets": ["category_keyword", "vendor_keyword"]
  198 +}
  199 +```
  200 +
  201 +**高级模式**(配置对象数组):
  202 +```json
  203 +{
  204 + "facets": [
  205 + {
  206 + "field": "category_keyword",
  207 + "size": 15,
  208 + "type": "terms"
  209 + },
  210 + {
  211 + "field": "min_price",
  212 + "type": "range",
  213 + "ranges": [
  214 + {"key": "0-50", "to": 50},
  215 + {"key": "50-100", "from": 50, "to": 100},
  216 + {"key": "100-200", "from": 100, "to": 200},
  217 + {"key": "200+", "from": 200}
  218 + ]
  219 + }
  220 + ]
  221 +}
  222 +```
  223 +
  224 +**分面配置参数**:
  225 +- `field`: 字段名(必填)
  226 +- `size`: 返回的分组数量(默认:10,范围:1-100)
  227 +- `type`: 分面类型,`terms`(分组统计)或 `range`(范围统计)
  228 +- `ranges`: 范围定义(仅当 type='range' 时需要)
  229 +
  230 +### 布尔表达式语法
  231 +
  232 +搜索查询支持布尔表达式,提供更灵活的搜索能力。
  233 +
  234 +**支持的操作符**:
  235 +
  236 +| 操作符 | 描述 | 示例 |
  237 +|--------|------|------|
  238 +| `AND` | 所有词必须匹配 | `玩具 AND 乐高` |
  239 +| `OR` | 任意词匹配 | `芭比 OR 娃娃` |
  240 +| `ANDNOT` | 排除特定词 | `玩具 ANDNOT 电动` |
  241 +| `RANK` | 排序加权(不强制匹配) | `玩具 RANK 乐高` |
  242 +| `()` | 分组 | `玩具 AND (乐高 OR 芭比)` |
  243 +
  244 +**操作符优先级**(从高到低):
  245 +1. `()` - 括号
  246 +2. `ANDNOT` - 排除
  247 +3. `AND` - 与
  248 +4. `OR` - 或
  249 +5. `RANK` - 排序
  250 +
  251 +**示例**:
  252 +```
  253 +"芭比娃娃" // 简单查询
  254 +"玩具 AND 乐高" // AND 查询
  255 +"芭比 OR 娃娃" // OR 查询
  256 +"玩具 ANDNOT 电动" // 排除查询
  257 +"玩具 AND (乐高 OR 芭比)" // 复杂查询
  258 +```
  259 +
  260 +---
  261 +
  262 +## 图片搜索接口
  263 +
  264 +### 接口信息
  265 +
  266 +- **端点**: `POST /search/image`
  267 +- **描述**: 基于图片相似度进行搜索,使用图片向量进行语义匹配
  268 +
  269 +### 请求参数
  270 +
  271 +```json
  272 +{
  273 + "image_url": "string (required)",
  274 + "size": 10,
  275 + "filters": {},
  276 + "range_filters": {}
  277 +}
  278 +```
  279 +
  280 +### 参数说明
  281 +
  282 +| 参数 | 类型 | 必填 | 默认值 | 描述 |
  283 +|------|------|------|--------|------|
  284 +| `image_url` | string | ✅ | - | 查询图片的 URL |
  285 +| `size` | integer | ❌ | 10 | 返回结果数量(1-100) |
  286 +| `filters` | object | ❌ | null | 精确匹配过滤器 |
  287 +| `range_filters` | object | ❌ | null | 数值范围过滤器 |
  288 +
  289 +### 请求示例
  290 +
  291 +```bash
  292 +curl -X POST "http://localhost:6002/search/image" \
  293 + -H "Content-Type: application/json" \
  294 + -d '{
  295 + "image_url": "https://example.com/barbie.jpg",
  296 + "size": 20,
  297 + "filters": {
  298 + "category_keyword": "玩具"
  299 + },
  300 + "range_filters": {
  301 + "min_price": {
  302 + "lte": 100
  303 + }
  304 + }
  305 + }'
  306 +```
  307 +
  308 +---
  309 +
  310 +## 响应格式说明
  311 +
  312 +### 标准响应结构
  313 +
  314 +```json
  315 +{
  316 + "results": [
  317 + {
  318 + "product_id": "12345",
  319 + "title": "芭比时尚娃娃",
  320 + "handle": "barbie-doll",
  321 + "description": "高品质芭比娃娃",
  322 + "vendor": "美泰",
  323 + "product_type": "玩具",
  324 + "tags": "娃娃, 玩具, 女孩",
  325 + "price": 89.99,
  326 + "compare_at_price": 129.99,
  327 + "currency": "USD",
  328 + "image_url": "https://example.com/image.jpg",
  329 + "in_stock": true,
  330 + "variants": [
  331 + {
  332 + "variant_id": "67890",
  333 + "title": "粉色款",
  334 + "price": 89.99,
  335 + "compare_at_price": 129.99,
  336 + "sku": "BARBIE-001",
  337 + "stock": 100,
  338 + "options": {
  339 + "option1": "粉色",
  340 + "option2": "标准款"
  341 + }
  342 + }
  343 + ],
  344 + "relevance_score": 8.5
  345 + }
  346 + ],
  347 + "total": 118,
  348 + "max_score": 8.5,
  349 + "facets": [
  350 + {
  351 + "field": "category_keyword",
  352 + "label": "category_keyword",
  353 + "type": "terms",
  354 + "values": [
  355 + {
  356 + "value": "玩具",
  357 + "label": "玩具",
  358 + "count": 85,
  359 + "selected": false
  360 + }
  361 + ]
  362 + }
  363 + ],
  364 + "query_info": {
  365 + "original_query": "芭比娃娃",
  366 + "detected_language": "zh",
  367 + "translations": {
  368 + "en": "barbie doll"
  369 + }
  370 + },
  371 + "suggestions": [],
  372 + "related_searches": [],
  373 + "took_ms": 45,
  374 + "performance_info": null,
  375 + "debug_info": null
  376 +}
  377 +```
  378 +
  379 +### 响应字段说明
  380 +
  381 +| 字段 | 类型 | 说明 |
  382 +|------|------|------|
  383 +| `results` | array | 搜索结果列表(ProductResult对象数组) |
  384 +| `results[].product_id` | string | 商品ID |
  385 +| `results[].title` | string | 商品标题 |
  386 +| `results[].price` | float | 价格(min_price) |
  387 +| `results[].variants` | array | 变体列表(SKU列表) |
  388 +| `results[].relevance_score` | float | 相关性分数 |
  389 +| `total` | integer | 匹配的总文档数 |
  390 +| `max_score` | float | 最高相关性分数 |
  391 +| `facets` | array | 分面统计结果 |
  392 +| `query_info` | object | 查询处理信息 |
  393 +| `took_ms` | integer | 搜索耗时(毫秒) |
  394 +
  395 +### ProductResult字段说明
  396 +
  397 +| 字段 | 类型 | 说明 |
  398 +|------|------|------|
  399 +| `product_id` | string | 商品ID(SPU ID) |
  400 +| `title` | string | 商品标题 |
  401 +| `handle` | string | 商品URL handle |
  402 +| `description` | string | 商品描述 |
  403 +| `vendor` | string | 供应商/品牌 |
  404 +| `product_type` | string | 商品类型 |
  405 +| `tags` | string | 标签 |
  406 +| `price` | float | 价格(min_price) |
  407 +| `compare_at_price` | float | 原价 |
  408 +| `currency` | string | 货币单位(默认USD) |
  409 +| `image_url` | string | 主图URL |
  410 +| `in_stock` | boolean | 是否有库存(任意变体有库存即为true) |
  411 +| `variants` | array | 变体列表 |
  412 +| `relevance_score` | float | 相关性分数 |
  413 +
  414 +### VariantResult字段说明
  415 +
  416 +| 字段 | 类型 | 说明 |
  417 +|------|------|------|
  418 +| `variant_id` | string | 变体ID(SKU ID) |
  419 +| `title` | string | 变体标题 |
  420 +| `price` | float | 价格 |
  421 +| `compare_at_price` | float | 原价 |
  422 +| `sku` | string | SKU编码 |
  423 +| `stock` | integer | 库存数量 |
  424 +| `options` | object | 选项(颜色、尺寸等) |
  425 +
  426 +---
  427 +
  428 +## 常见场景示例
  429 +
  430 +### 场景1:商品列表页搜索
  431 +
  432 +**需求**: 搜索"玩具",按价格从低到高排序,显示前20个结果
  433 +
  434 +```json
  435 +{
  436 + "query": "玩具",
  437 + "size": 20,
  438 + "from": 0,
  439 + "sort_by": "min_price",
  440 + "sort_order": "asc"
  441 +}
  442 +```
  443 +
  444 +### 场景2:带筛选的商品搜索
  445 +
  446 +**需求**: 搜索"玩具",筛选类目为"益智玩具",价格在50-200之间
  447 +
  448 +```json
  449 +{
  450 + "query": "玩具",
  451 + "size": 20,
  452 + "filters": {
  453 + "category_keyword": "益智玩具"
  454 + },
  455 + "range_filters": {
  456 + "min_price": {
  457 + "gte": 50,
  458 + "lte": 200
  459 + }
  460 + }
  461 +}
  462 +```
  463 +
  464 +### 场景3:带分面的商品搜索
  465 +
  466 +**需求**: 搜索"玩具",获取类目和品牌的分面统计,用于构建筛选器
  467 +
  468 +```json
  469 +{
  470 + "query": "玩具",
  471 + "size": 20,
  472 + "facets": [
  473 + "category_keyword",
  474 + "vendor_keyword"
  475 + ]
  476 +}
  477 +```
  478 +
  479 +### 场景4:多条件组合搜索
  480 +
  481 +**需求**: 搜索"玩具",筛选多个品牌,价格范围,并获取分面统计
  482 +
  483 +```json
  484 +{
  485 + "query": "玩具",
  486 + "size": 20,
  487 + "filters": {
  488 + "vendor_keyword": ["乐高", "孩之宝", "美泰"]
  489 + },
  490 + "range_filters": {
  491 + "min_price": {
  492 + "gte": 50,
  493 + "lte": 200
  494 + }
  495 + },
  496 + "facets": [
  497 + {
  498 + "field": "category_keyword",
  499 + "size": 15
  500 + },
  501 + {
  502 + "field": "min_price",
  503 + "type": "range",
  504 + "ranges": [
  505 + {"key": "0-50", "to": 50},
  506 + {"key": "50-100", "from": 50, "to": 100},
  507 + {"key": "100-200", "from": 100, "to": 200},
  508 + {"key": "200+", "from": 200}
  509 + ]
  510 + }
  511 + ],
  512 + "sort_by": "min_price",
  513 + "sort_order": "asc"
  514 +}
  515 +```
  516 +
  517 +### 场景5:布尔表达式搜索
  518 +
  519 +**需求**: 搜索包含"玩具"和"乐高"的商品,排除"电动"
  520 +
  521 +```json
  522 +{
  523 + "query": "玩具 AND 乐高 ANDNOT 电动",
  524 + "size": 20
  525 +}
  526 +```
  527 +
  528 +### 场景6:分页查询
  529 +
  530 +**需求**: 获取第2页结果(每页20条)
  531 +
  532 +```json
  533 +{
  534 + "query": "玩具",
  535 + "size": 20,
  536 + "from": 20
  537 +}
  538 +```
  539 +
  540 +---
  541 +
  542 +## 错误处理
  543 +
  544 +### 错误响应格式
  545 +
  546 +```json
  547 +{
  548 + "error": "错误信息",
  549 + "detail": "详细错误信息(可选)"
  550 +}
  551 +```
  552 +
  553 +### 常见错误码
  554 +
  555 +| HTTP状态码 | 说明 | 处理建议 |
  556 +|-----------|------|---------|
  557 +| 200 | 成功 | - |
  558 +| 400 | 请求参数错误 | 检查请求参数格式和必填字段 |
  559 +| 404 | 接口不存在 | 检查接口路径 |
  560 +| 500 | 服务器内部错误 | 联系技术支持 |
  561 +
  562 +### 错误处理示例
  563 +
  564 +**Python**:
  565 +```python
  566 +import requests
  567 +
  568 +try:
  569 + response = requests.post(url, json=payload, timeout=10)
  570 + response.raise_for_status()
  571 + data = response.json()
  572 +except requests.exceptions.HTTPError as e:
  573 + print(f"HTTP错误: {e}")
  574 + if response.status_code == 400:
  575 + error_data = response.json()
  576 + print(f"错误详情: {error_data.get('detail')}")
  577 +except requests.exceptions.RequestException as e:
  578 + print(f"请求异常: {e}")
  579 +```
  580 +
  581 +**JavaScript**:
  582 +```javascript
  583 +try {
  584 + const response = await fetch(url, {
  585 + method: 'POST',
  586 + headers: { 'Content-Type': 'application/json' },
  587 + body: JSON.stringify(payload)
  588 + });
  589 +
  590 + if (!response.ok) {
  591 + const error = await response.json();
  592 + throw new Error(error.error || `HTTP ${response.status}`);
  593 + }
  594 +
  595 + const data = await response.json();
  596 +} catch (error) {
  597 + console.error('搜索失败:', error.message);
  598 +}
  599 +```
  600 +
  601 +---
  602 +
  603 +### 5. 代码示例
  604 +
  605 +**完整的搜索函数(Python)**:
  606 +
  607 +```python
  608 +import requests
  609 +from typing import Dict, Any, Optional, List
  610 +
  611 +class SearchClient:
  612 + def __init__(self, base_url: str = "http://localhost:6002"):
  613 + self.base_url = base_url
  614 + self.timeout = 10
  615 +
  616 + def search(
  617 + self,
  618 + query: str,
  619 + size: int = 20,
  620 + from_: int = 0,
  621 + filters: Optional[Dict] = None,
  622 + range_filters: Optional[Dict] = None,
  623 + facets: Optional[List] = None,
  624 + sort_by: Optional[str] = None,
  625 + sort_order: str = "desc"
  626 + ) -> Dict[str, Any]:
  627 + """
  628 + 执行搜索查询
  629 +
  630 + Args:
  631 + query: 搜索查询字符串
  632 + size: 返回结果数量
  633 + from_: 分页偏移量
  634 + filters: 精确匹配过滤器
  635 + range_filters: 范围过滤器
  636 + facets: 分面配置
  637 + sort_by: 排序字段
  638 + sort_order: 排序方向
  639 +
  640 + Returns:
  641 + 搜索结果字典
  642 + """
  643 + url = f"{self.base_url}/search/"
  644 + payload = {
  645 + "query": query,
  646 + "size": size,
  647 + "from": from_,
  648 + }
  649 +
  650 + if filters:
  651 + payload["filters"] = filters
  652 + if range_filters:
  653 + payload["range_filters"] = range_filters
  654 + if facets:
  655 + payload["facets"] = facets
  656 + if sort_by:
  657 + payload["sort_by"] = sort_by
  658 + payload["sort_order"] = sort_order
  659 +
  660 + try:
  661 + response = requests.post(
  662 + url,
  663 + json=payload,
  664 + timeout=self.timeout
  665 + )
  666 + response.raise_for_status()
  667 + return response.json()
  668 + except requests.exceptions.RequestException as e:
  669 + raise Exception(f"搜索请求失败: {e}")
  670 +
  671 +# 使用示例
  672 +client = SearchClient()
  673 +result = client.search(
  674 + query="玩具",
  675 + size=20,
  676 + filters={"category_keyword": "益智玩具"},
  677 + range_filters={"min_price": {"gte": 50, "lte": 200}},
  678 + facets=["category_keyword", "vendor_keyword"],
  679 + sort_by="min_price",
  680 + sort_order="asc"
  681 +)
  682 +
  683 +print(f"找到 {result['total']} 个结果")
  684 +for product in result['results']:
  685 + print(f"{product['title']} - ¥{product['price']}")
  686 +```
  687 +
  688 +**完整的搜索函数(JavaScript)**:
  689 +
  690 +```javascript
  691 +class SearchClient {
  692 + constructor(baseUrl = 'http://localhost:6002') {
  693 + this.baseUrl = baseUrl;
  694 + this.timeout = 10000;
  695 + }
  696 +
  697 + async search({
  698 + query,
  699 + size = 20,
  700 + from = 0,
  701 + filters = null,
  702 + rangeFilters = null,
  703 + facets = null,
  704 + sortBy = null,
  705 + sortOrder = 'desc'
  706 + }) {
  707 + const url = `${this.baseUrl}/search/`;
  708 + const payload = {
  709 + query,
  710 + size,
  711 + from,
  712 + };
  713 +
  714 + if (filters) payload.filters = filters;
  715 + if (rangeFilters) payload.range_filters = rangeFilters;
  716 + if (facets) payload.facets = facets;
  717 + if (sortBy) {
  718 + payload.sort_by = sortBy;
  719 + payload.sort_order = sortOrder;
  720 + }
  721 +
  722 + try {
  723 + const response = await fetch(url, {
  724 + method: 'POST',
  725 + headers: {
  726 + 'Content-Type': 'application/json'
  727 + },
  728 + body: JSON.stringify(payload),
  729 + signal: AbortSignal.timeout(this.timeout)
  730 + });
  731 +
  732 + if (!response.ok) {
  733 + const error = await response.json();
  734 + throw new Error(error.error || `HTTP ${response.status}`);
  735 + }
  736 +
  737 + return await response.json();
  738 + } catch (error) {
  739 + throw new Error(`搜索请求失败: ${error.message}`);
  740 + }
  741 + }
  742 +}
  743 +
  744 +// 使用示例
  745 +const client = new SearchClient();
  746 +const result = await client.search({
  747 + query: '玩具',
  748 + size: 20,
  749 + filters: { category_keyword: '益智玩具' },
  750 + rangeFilters: { min_price: { gte: 50, lte: 200 } },
  751 + facets: ['category_keyword', 'vendor_keyword'],
  752 + sortBy: 'min_price',
  753 + sortOrder: 'asc'
  754 +});
  755 +
  756 +console.log(`找到 ${result.total} 个结果`);
  757 +result.results.forEach(product => {
  758 + console.log(`${product.title} - ¥${product.price}`);
  759 +});
  760 +```
  761 +
  762 +---
  763 +
  764 +## 附录
  765 +
  766 +### 常用字段列表
  767 +
  768 +#### 过滤字段(使用 `*_keyword` 后缀)
  769 +
  770 +- `category_keyword`: 类目
  771 +- `vendor_keyword`: 品牌/供应商
  772 +- `product_type_keyword`: 商品类型
  773 +- `tags_keyword`: 标签
  774 +
  775 +#### 范围字段
  776 +
  777 +- `min_price`: 最低价格
  778 +- `max_price`: 最高价格
  779 +- `compare_at_price`: 原价
  780 +- `create_time`: 创建时间
  781 +- `update_time`: 更新时间
  782 +
  783 +#### 排序字段
  784 +
  785 +- `min_price`: 最低价格
  786 +- `max_price`: 最高价格
  787 +- `title`: 标题(字母序)
  788 +- `create_time`: 创建时间
  789 +- `update_time`: 更新时间
  790 +- `relevance_score`: 相关性分数(默认)
  791 +
ES_QUERY_RESTRUCTURE_COMPLETE.md
@@ -218,7 +218,7 @@ from .rerank_engine import RerankEngine # 原 RankingEngine @@ -218,7 +218,7 @@ from .rerank_engine import RerankEngine # 原 RankingEngine
218 }, 218 },
219 { 219 {
220 "knn": { 220 "knn": {
221 - "field": "name_embedding", 221 + "field": "title_embedding",
222 "query_vector": [...], 222 "query_vector": [...],
223 "k": 50, 223 "k": 50,
224 "num_candidates": 200 224 "num_candidates": 200
HighLevelDesign.md
@@ -51,112 +51,6 @@ updater varchar(64) @@ -51,112 +51,6 @@ updater varchar(64)
51 update_time datetime 51 update_time datetime
52 deleted bit(1) 52 deleted bit(1)
53 53
54 -所有租户共用这个主表  
55 -  
56 -### 每个租户的辅表  
57 -各个租户,有自己的扩展表。 入索引的时候,商品主表 shoplazza_product_sku 的 id + shopid,拼接租户自己单独的扩展表(比如可以放一些自己的属性体系、各种语言的商品名、品牌名、标签、分类等)  
58 -  
59 -但是,各个租户,可能有不一样的业务数据,比如不同租户有不同的属性的体系、不同语言的商品标题(一般至少有中英文两种满足跨境的搜索需求),有不同的权重(提权)字段、业务过滤和聚合字段。  
60 -能够统一的 只能是 sku表 按照一套配置规范、做一个配置文件,按照配置文件建设ES mapping结构以及做数据的入库。  
61 -  
62 -## SearchEngine  
63 -  
64 -### IndexerConfig  
65 - @阿里opensearch电商行业.md, 有两套配置  
66 -1. 应用结构配置 : 定义了ES的输入数据有哪些字段、关联mysql的哪些字段.  
67 -2. 索引结构配置 : 定义了ES的字段,每个字段的索引mapping配置,支持各个域的查询,包括默认的域的查询。索引配置预定一号了一堆分析方式 由 @商品数据源入ES配置规范.md 定义。  
68 -  
69 -  
70 -  
71 -### 测试数据灌入  
72 -  
73 -灌入数据、mysql到ES的自动同步,不在本项目的范围内,另外有java项目负责。  
74 -但是,该项目 为了提供测试数据,需要 构造一个实例 tenant1.  
75 -我们为他构造一套应用配置和索引配置。  
76 -灌入一批测试数据,可以些一个简单的 全量灌入的实现。  
77 -数据源地址在:data/tenant1/goods_with_pic.5years_congku.csv.shuf.1w  
78 -请根据这里面的字段,建设辅助表(注意看哪些字段在主表有,哪些需要放到辅表)  
79 -然后写一个程序,将数据分别灌入主表和辅表。  
80 -  
81 -  
82 -### queryParser  
83 -query分析,做以下几个事情:  
84 -  
85 -1. 查询改写。 配置词典的key是query,value是改写后的查询表达式,比如。比如品牌词 改写为在brand|query OR name|query,类别词、标签词等都可以放进去。纠错、规范化、查询改写等 都可以通过这个词典来配置。  
86 -2. 翻译。配置需要得到的几种目标语言。 在tenant1测试案例中,我们配置 zh en两种语言。先对query做语言检测,如果query是中文那么要翻译一下en,如果是en那么要翻译zh,如果两者都不是那么zh en都需要翻译。  
87 -3. 如果配置打开了text_embedding查询,并且query 包含了default域的查询,那么要把default域的查询词转向量,后面searcher会用这个向量参与查询。  
88 -翻译代码参考:  
89 -```  
90 -import requests  
91 -api_url = "https://api.deepl.com/v2/translate"  
92 -headers = {  
93 - "Authorization": "DeepL-Auth-Key YOUR_AUTH_KEY",  
94 - "Content-Type": "application/json",  
95 -}  
96 -payload = {  
97 - "text": ["要翻译的文本"],  
98 - "target_lang": "ZH", # 中文  
99 -}  
100 -  
101 -response = requests.post(api_url, headers=headers, json=payload, timeout=10)  
102 -  
103 -if response.status_code == 200:  
104 - data = response.json()  
105 - translation = data["translations"][0]["text"]  
106 - print(translation)  
107 -```  
108 -  
109 -### searcher  
110 -  
111 -支持多种检索表达式:  
112 -支持多种匹配方式,如AND、OR、RANK、NOTAND以及(),优先级从高到低为(),ANDNOT,AND,OR,RANK。  
113 -  
114 -default域的相关性,是代码里面单独计算,是特定的深度定制优化的,暂时不做配置化。  
115 -  
116 -暂时具体实现为 bm25()+0.2*text_embedding_relevence(也就是knn检索表达式的打分)  
117 -bm25() 包括多语言的打分:内部需要通过配置翻译为多种语言(配置几种目标语言 默认中文、英文,并且设置对应的检索域),然后分别到对应的字段搜索,中文字段到配置的中文title搜索,英文到对应的英文title搜索。  
118 -bm25打分(base_query):  
119 -"multi_match": {  
120 - "query": search_query,  
121 - "fields": match_fields,  
122 - "minimum_should_match": "67%",  
123 - "tie_breaker": 0.9,  
124 - "boost": 1.0, # Low boost for auxiliary keyword query  
125 - "_name": "base_query"  
126 -}  
127 -  
128 -text_embedding_relevence:  
129 - knn_query = {  
130 - "knn": {  
131 - "field": text_embedding_field,  
132 - "query_vector": query_vector.tolist(),  
133 - "k": KNN_K,  
134 - "num_candidates": KNN_NUM_CANDIDATES  
135 - }  
136 - }  
137 -  
138 -支持配置化的排序打分:  
139 -default域 支持配置的排序方式:  
140 -| 场景 | 表达式 | 含义 |  
141 -|------|--------|------|  
142 -| 电商 | `text_re()+general_score*2+timeliness(end_time)` | 文本分、宝贝综合分值、过期时间 |  
143 -  
144 -  
145 -有一个配置,是否按照spu聚合,如果打开spu聚合,那么 要配置spu_id的字段,检索表达上需要加上:  
146 -es_query["aggs"]["unique_count"] = {  
147 - "cardinality": {  
148 - "field": spu_id_field_name  
149 - }  
150 -}  
151 -es_query["collapse"]["inner_hits"] = {  
152 - "_source": False,  
153 - "name": "top_docs",  
154 - "size": INNER_HITS_SIZE  
155 -}  
156 -  
157 -  
158 -## 相关配置  
159 -  
160 ES_CONFIG = { 54 ES_CONFIG = {
161 'host': 'http://localhost:9200', 55 'host': 'http://localhost:9200',
162 'username': 'essa', 56 'username': 'essa',
INDEX_FIELDS_DOCUMENTATION.md 0 → 100644
@@ -0,0 +1,223 @@ @@ -0,0 +1,223 @@
  1 +# 索引字段说明文档
  2 +
  3 +本文档详细说明了 Elasticsearch 索引中所有字段的类型、索引方式、数据来源等信息。
  4 +
  5 +## 索引基本信息
  6 +
  7 +- **索引名称**: `search_products`
  8 +- **索引级别**: SPU级别(商品级别)
  9 +- **数据结构**: SPU文档包含嵌套的variants(SKU)数组
  10 +
  11 +## 字段说明表
  12 +
  13 +### 基础字段
  14 +
  15 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | 说明 |
  16 +|-----------|-----------|---------|---------|-----------|-----------|-------------|------|
  17 +| tenant_id | KEYWORD | 是 | 精确匹配 | SPU表 | tenant_id | BIGINT | 租户ID,用于多租户隔离 |
  18 +| product_id | KEYWORD | 是 | 精确匹配 | SPU表 | id | BIGINT | 商品ID(SPU ID) |
  19 +| handle | KEYWORD | 是 | 精确匹配 | SPU表 | handle | VARCHAR(255) | 商品URL handle |
  20 +
  21 +### 文本搜索字段
  22 +
  23 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | Boost权重 | 说明 |
  24 +|-----------|-----------|---------|---------|-----------|-----------|-------------|-----------|------|
  25 +| title | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | title | VARCHAR(512) | 3.0 | 商品标题,权重最高 |
  26 +| brief | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | brief | VARCHAR(512) | 1.5 | 商品简介 |
  27 +| description | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | description | TEXT | 1.0 | 商品详细描述 |
  28 +
  29 +### SEO字段
  30 +
  31 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | Boost权重 | 是否返回 | 说明 |
  32 +|-----------|-----------|---------|---------|-----------|-----------|-------------|-----------|---------|------|
  33 +| seo_title | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | seo_title | VARCHAR(512) | 2.0 | 否 | SEO标题,用于提升相关性 |
  34 +| seo_description | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | seo_description | TEXT | 1.5 | 否 | SEO描述 |
  35 +| seo_keywords | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | seo_keywords | VARCHAR(1024) | 2.0 | 否 | SEO关键词 |
  36 +
  37 +### 分类和标签字段
  38 +
  39 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | Boost权重 | 是否返回 | 说明 |
  40 +|-----------|-----------|---------|---------|-----------|-----------|-------------|-----------|---------|------|
  41 +| vendor | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | vendor | VARCHAR(255) | 1.5 | 是 | 供应商/品牌(文本搜索) |
  42 +| vendor_keyword | KEYWORD | 是 | 精确匹配 | SPU表 | vendor | VARCHAR(255) | - | 否 | 供应商/品牌(精确匹配,用于过滤) |
  43 +| product_type | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | category | VARCHAR(255) | 1.5 | 是 | 商品类型(文本搜索) |
  44 +| product_type_keyword | KEYWORD | 是 | 精确匹配 | SPU表 | category | VARCHAR(255) | - | 否 | 商品类型(精确匹配,用于过滤) |
  45 +| tags | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | tags | VARCHAR(1024) | 1.0 | 是 | 标签(文本搜索) |
  46 +| tags_keyword | KEYWORD | 是 | 精确匹配 | SPU表 | tags | VARCHAR(1024) | - | 否 | 标签(精确匹配,用于过滤) |
  47 +| category | TEXT | 是 | chinese_ecommerce分析器 | SPU表 | category | VARCHAR(255) | 1.5 | 是 | 类目(文本搜索) |
  48 +| category_keyword | KEYWORD | 是 | 精确匹配 | SPU表 | category | VARCHAR(255) | - | 否 | 类目(精确匹配,用于过滤) |
  49 +
  50 +### 价格字段
  51 +
  52 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | 说明 |
  53 +|-----------|-----------|---------|---------|-----------|-----------|-------------|------|
  54 +| min_price | FLOAT | 是 | 数值范围 | SKU表(聚合计算) | price | DECIMAL(10,2) | 最低价格(从所有SKU中取最小值) |
  55 +| max_price | FLOAT | 是 | 数值范围 | SKU表(聚合计算) | price | DECIMAL(10,2) | 最高价格(从所有SKU中取最大值) |
  56 +| compare_at_price | FLOAT | 是 | 数值范围 | SKU表(聚合计算) | compare_at_price | DECIMAL(10,2) | 原价(从所有SKU中取最大值) |
  57 +
  58 +**价格计算逻辑**:
  59 +- `min_price`: 取该SPU下所有SKU的price字段的最小值
  60 +- `max_price`: 取该SPU下所有SKU的price字段的最大值
  61 +- `compare_at_price`: 取该SPU下所有SKU的compare_at_price字段的最大值(如果存在)
  62 +
  63 +### 图片字段
  64 +
  65 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | 说明 |
  66 +|-----------|-----------|---------|---------|-----------|-----------|-------------|------|
  67 +| image_url | KEYWORD | 否 | 不索引 | SPU表 | image_src | VARCHAR(500) | 商品主图URL,仅用于展示 |
  68 +
  69 +### 文本嵌入字段
  70 +
  71 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | 说明 |
  72 +|-----------|-----------|---------|---------|-----------|-----------|-------------|------|
  73 +| title_embedding | TEXT_EMBEDDING | 是 | 向量相似度(dot_product) | 计算生成 | title | VARCHAR(512) | 标题的文本向量(1024维),用于语义搜索 |
  74 +
  75 +**说明**:
  76 +- 向量维度:1024
  77 +- 相似度算法:dot_product(点积)
  78 +- 数据来源:基于title字段通过BGE-M3模型生成
  79 +
  80 +### 时间字段
  81 +
  82 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | 是否返回 | 说明 |
  83 +|-----------|-----------|---------|---------|-----------|-----------|-------------|---------|------|
  84 +| create_time | DATE | 是 | 日期范围 | SPU表 | create_time | DATETIME | 是 | 创建时间 |
  85 +| update_time | DATE | 是 | 日期范围 | SPU表 | update_time | DATETIME | 是 | 更新时间 |
  86 +| shoplazza_created_at | DATE | 是 | 日期范围 | SPU表 | shoplazza_created_at | DATETIME | 否 | 店匠系统创建时间 |
  87 +| shoplazza_updated_at | DATE | 是 | 日期范围 | SPU表 | shoplazza_updated_at | DATETIME | 否 | 店匠系统更新时间 |
  88 +
  89 +### 嵌套Variants字段(SKU级别)
  90 +
  91 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | 说明 |
  92 +|-----------|-----------|---------|---------|-----------|-----------|-------------|------|
  93 +| variants | JSON (nested) | 是 | 嵌套对象 | SKU表 | - | - | 商品变体数组(嵌套结构) |
  94 +
  95 +#### Variants子字段
  96 +
  97 +| 索引字段名 | ES字段类型 | 是否索引 | 索引方式 | 数据来源表 | 表中字段名 | 表中字段类型 | 说明 |
  98 +|-----------|-----------|---------|---------|-----------|-----------|-------------|------|
  99 +| variants.variant_id | keyword | 是 | 精确匹配 | SKU表 | id | BIGINT | 变体ID(SKU ID) |
  100 +| variants.title | text | 是 | chinese_ecommerce分析器 | SKU表 | title | VARCHAR(500) | 变体标题 |
  101 +| variants.price | float | 是 | 数值范围 | SKU表 | price | DECIMAL(10,2) | 变体价格 |
  102 +| variants.compare_at_price | float | 是 | 数值范围 | SKU表 | compare_at_price | DECIMAL(10,2) | 变体原价 |
  103 +| variants.sku | keyword | 是 | 精确匹配 | SKU表 | sku | VARCHAR(100) | SKU编码 |
  104 +| variants.stock | long | 是 | 数值范围 | SKU表 | inventory_quantity | INT(11) | 库存数量 |
  105 +| variants.options | object | 是 | 对象 | SKU表 | option1/option2/option3 | VARCHAR(255) | 选项(颜色、尺寸等) |
  106 +
  107 +**Variants结构说明**:
  108 +- `variants` 是一个嵌套对象数组,每个元素代表一个SKU
  109 +- 使用ES的nested类型,支持对嵌套字段进行独立查询和过滤
  110 +- `options` 对象包含 `option1`、`option2`、`option3` 三个字段,分别对应SKU表中的选项值
  111 +
  112 +## 字段类型说明
  113 +
  114 +### ES字段类型映射
  115 +
  116 +| ES字段类型 | Elasticsearch映射 | 用途 |
  117 +|-----------|------------------|------|
  118 +| KEYWORD | keyword | 精确匹配、过滤、聚合、排序 |
  119 +| TEXT | text | 全文检索(支持分词) |
  120 +| FLOAT | float | 浮点数(价格、权重等) |
  121 +| LONG | long | 整数(库存、计数等) |
  122 +| DATE | date | 日期时间 |
  123 +| TEXT_EMBEDDING | dense_vector | 文本向量(1024维) |
  124 +| JSON | object/nested | 嵌套对象 |
  125 +
  126 +### 分析器说明
  127 +
  128 +| 分析器名称 | 语言 | 说明 |
  129 +|-----------|------|------|
  130 +| chinese_ecommerce | 中文 | Ansj中文分词器(电商优化),用于中文文本的分词和搜索 |
  131 +
  132 +## 索引配置
  133 +
  134 +### 索引设置
  135 +
  136 +- **分片数**: 1
  137 +- **副本数**: 0
  138 +- **刷新间隔**: 30秒
  139 +
  140 +### 查询域(Query Domains)
  141 +
  142 +系统定义了多个查询域,用于在不同场景下搜索不同的字段组合:
  143 +
  144 +1. **default(默认索引)**: 搜索所有文本字段
  145 + - 包含字段:title, brief, description, seo_title, seo_description, seo_keywords, vendor, product_type, tags, category
  146 + - Boost: 1.0
  147 +
  148 +2. **title(标题索引)**: 仅搜索标题相关字段
  149 + - 包含字段:title, seo_title
  150 + - Boost: 2.0
  151 +
  152 +3. **vendor(品牌索引)**: 仅搜索品牌字段
  153 + - 包含字段:vendor
  154 + - Boost: 1.5
  155 +
  156 +4. **category(类目索引)**: 仅搜索类目字段
  157 + - 包含字段:category
  158 + - Boost: 1.5
  159 +
  160 +5. **tags(标签索引)**: 搜索标签和SEO关键词
  161 + - 包含字段:tags, seo_keywords
  162 + - Boost: 1.0
  163 +
  164 +## 数据转换规则
  165 +
  166 +### 数据类型转换
  167 +
  168 +1. **BIGINT → KEYWORD**: 数字ID转换为字符串(如 `product_id`, `variant_id`)
  169 +2. **DECIMAL → FLOAT**: 价格字段从DECIMAL转换为FLOAT
  170 +3. **INT → LONG**: 库存数量从INT转换为LONG
  171 +4. **DATETIME → DATE**: 时间字段转换为ISO格式字符串
  172 +
  173 +### 特殊处理
  174 +
  175 +1. **价格聚合**: 从多个SKU的价格中计算min_price、max_price、compare_at_price
  176 +2. **图片URL处理**: 如果image_src不是完整URL,会自动添加协议前缀
  177 +3. **选项合并**: 将SKU表的option1、option2、option3合并为options对象
  178 +
  179 +## 注意事项
  180 +
  181 +1. **多租户隔离**: 所有查询必须包含 `tenant_id` 过滤条件
  182 +2. **嵌套查询**: 查询variants字段时需要使用nested查询语法
  183 +3. **字段命名**: 用于过滤的字段应使用 `*_keyword` 后缀的字段
  184 +4. **向量搜索**: title_embedding字段用于语义搜索,需要配合文本查询使用
  185 +5. **Boost权重**: 不同字段的boost权重影响搜索结果的相关性排序
  186 +
  187 +## 数据来源表结构
  188 +
  189 +### SPU表(shoplazza_product_spu)
  190 +
  191 +主要字段:
  192 +- `id`: BIGINT - 主键ID
  193 +- `tenant_id`: BIGINT - 租户ID
  194 +- `handle`: VARCHAR(255) - URL handle
  195 +- `title`: VARCHAR(512) - 商品标题
  196 +- `brief`: VARCHAR(512) - 商品简介
  197 +- `description`: TEXT - 商品描述
  198 +- `vendor`: VARCHAR(255) - 供应商/品牌
  199 +- `category`: VARCHAR(255) - 类目
  200 +- `tags`: VARCHAR(1024) - 标签
  201 +- `seo_title`: VARCHAR(512) - SEO标题
  202 +- `seo_description`: TEXT - SEO描述
  203 +- `seo_keywords`: VARCHAR(1024) - SEO关键词
  204 +- `image_src`: VARCHAR(500) - 图片URL
  205 +- `create_time`: DATETIME - 创建时间
  206 +- `update_time`: DATETIME - 更新时间
  207 +- `shoplazza_created_at`: DATETIME - 店匠创建时间
  208 +- `shoplazza_updated_at`: DATETIME - 店匠更新时间
  209 +
  210 +### SKU表(shoplazza_product_sku)
  211 +
  212 +主要字段:
  213 +- `id`: BIGINT - 主键ID(对应variant_id)
  214 +- `spu_id`: BIGINT - SPU ID(关联字段)
  215 +- `title`: VARCHAR(500) - 变体标题
  216 +- `price`: DECIMAL(10,2) - 价格
  217 +- `compare_at_price`: DECIMAL(10,2) - 原价
  218 +- `sku`: VARCHAR(100) - SKU编码
  219 +- `inventory_quantity`: INT(11) - 库存数量
  220 +- `option1`: VARCHAR(255) - 选项1
  221 +- `option2`: VARCHAR(255) - 选项2
  222 +- `option3`: VARCHAR(255) - 选项3
  223 +
config/config.yaml
@@ -169,7 +169,7 @@ fields: @@ -169,7 +169,7 @@ fields:
169 return_in_source: true 169 return_in_source: true
170 170
171 # 文本嵌入字段(用于语义搜索) 171 # 文本嵌入字段(用于语义搜索)
172 - - name: "name_embedding" 172 + - name: "title_embedding"
173 type: "TEXT_EMBEDDING" 173 type: "TEXT_EMBEDDING"
174 embedding_dims: 1024 174 embedding_dims: 1024
175 embedding_similarity: "dot_product" 175 embedding_similarity: "dot_product"
@@ -296,7 +296,7 @@ query_config: @@ -296,7 +296,7 @@ query_config:
296 enable_query_rewrite: true 296 enable_query_rewrite: true
297 297
298 # Embedding field names (if not set, will auto-detect from fields) 298 # Embedding field names (if not set, will auto-detect from fields)
299 - text_embedding_field: "name_embedding" # Field name for text embeddings 299 + text_embedding_field: "title_embedding" # Field name for text embeddings
300 image_embedding_field: null # Field name for image embeddings (if not set, will auto-detect) 300 image_embedding_field: null # Field name for image embeddings (if not set, will auto-detect)
301 301
302 # Translation API (DeepL) 302 # Translation API (DeepL)
config/config_loader.py
@@ -55,7 +55,7 @@ class QueryConfig: @@ -55,7 +55,7 @@ class QueryConfig:
55 translation_context: str = "e-commerce product search" # Context hint for translation 55 translation_context: str = "e-commerce product search" # Context hint for translation
56 56
57 # Embedding field names - if not set, will auto-detect from fields 57 # Embedding field names - if not set, will auto-detect from fields
58 - text_embedding_field: Optional[str] = None # Field name for text embeddings (e.g., "name_embedding") 58 + text_embedding_field: Optional[str] = None # Field name for text embeddings (e.g., "title_embedding")
59 image_embedding_field: Optional[str] = None # Field name for image embeddings (e.g., "image_embedding") 59 image_embedding_field: Optional[str] = None # Field name for image embeddings (e.g., "image_embedding")
60 60
61 # ES source fields configuration - fields to return in search results 61 # ES source fields configuration - fields to return in search results