diff --git a/docs/搜索API对接指南.md b/docs/搜索API对接指南.md index 3cb0546..73f34c9 100644 --- a/docs/搜索API对接指南.md +++ b/docs/搜索API对接指南.md @@ -35,31 +35,36 @@ curl -X POST "http://localhost:6002/search/" \ }' ``` -### Python示例 +### curl示例:带过滤与分页 -```python -import requests - -url = "http://localhost:6002/search/" -response = requests.post(url, json={"query": "芭比娃娃"}) -data = response.json() -print(f"找到 {data['total']} 个结果") +```bash +curl -X POST "http://localhost:6002/search/" \ + -H "Content-Type: application/json" \ + -d '{ + "tenant_id": "demo-tenant", + "query": "芭比娃娃 AND 配件", + "size": 5, + "from": 10, + "filters": { + "vendor_keyword": ["乐高", "孩之宝"] + }, + "sort_by": "min_price", + "sort_order": "asc" + }' ``` -### JavaScript示例 +### curl示例:开启调试与分面 -```javascript -const response = await fetch('http://localhost:6002/search/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - query: '芭比娃娃' - }) -}); -const data = await response.json(); -console.log(`找到 ${data.total} 个结果`); +```bash +curl -X POST "http://localhost:6002/search/" \ + -H "Content-Type: application/json" \ + -d '{ + "tenant_id": "demo-tenant", + "query": "芭比娃娃", + "facets": ["category_keyword", "vendor_keyword"], + "min_score": 0.2, + "debug": true + }' ``` --- @@ -89,6 +94,7 @@ console.log(`找到 ${data.total} 个结果`); ```json { + "tenant_id": "string (required)", "query": "string (required)", "size": 10, "from": 0, @@ -108,6 +114,7 @@ console.log(`找到 ${data.total} 个结果`); | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| +| `tenant_id` | string | ✅ | - | 租户ID,用于隔离不同站点或客户的数据 | | `query` | string | ✅ | - | 搜索查询字符串,支持布尔表达式(AND, OR, RANK, ANDNOT) | | `size` | integer | ❌ | 10 | 返回结果数量(1-100) | | `from` | integer | ❌ | 0 | 分页偏移量(用于分页) | @@ -539,226 +546,6 @@ curl -X POST "http://localhost:6002/search/image" \ --- -## 错误处理 - -### 错误响应格式 - -```json -{ - "error": "错误信息", - "detail": "详细错误信息(可选)" -} -``` - -### 常见错误码 - -| HTTP状态码 | 说明 | 处理建议 | -|-----------|------|---------| -| 200 | 成功 | - | -| 400 | 请求参数错误 | 检查请求参数格式和必填字段 | -| 404 | 接口不存在 | 检查接口路径 | -| 500 | 服务器内部错误 | 联系技术支持 | - -### 错误处理示例 - -**Python**: -```python -import requests - -try: - response = requests.post(url, json=payload, timeout=10) - response.raise_for_status() - data = response.json() -except requests.exceptions.HTTPError as e: - print(f"HTTP错误: {e}") - if response.status_code == 400: - error_data = response.json() - print(f"错误详情: {error_data.get('detail')}") -except requests.exceptions.RequestException as e: - print(f"请求异常: {e}") -``` - -**JavaScript**: -```javascript -try { - const response = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload) - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.error || `HTTP ${response.status}`); - } - - const data = await response.json(); -} catch (error) { - console.error('搜索失败:', error.message); -} -``` - ---- - -### 5. 代码示例 - -**完整的搜索函数(Python)**: - -```python -import requests -from typing import Dict, Any, Optional, List - -class SearchClient: - def __init__(self, base_url: str = "http://localhost:6002"): - self.base_url = base_url - self.timeout = 10 - - def search( - self, - query: str, - size: int = 20, - from_: int = 0, - filters: Optional[Dict] = None, - range_filters: Optional[Dict] = None, - facets: Optional[List] = None, - sort_by: Optional[str] = None, - sort_order: str = "desc" - ) -> Dict[str, Any]: - """ - 执行搜索查询 - - Args: - query: 搜索查询字符串 - size: 返回结果数量 - from_: 分页偏移量 - filters: 精确匹配过滤器 - range_filters: 范围过滤器 - facets: 分面配置 - sort_by: 排序字段 - sort_order: 排序方向 - - Returns: - 搜索结果字典 - """ - url = f"{self.base_url}/search/" - payload = { - "query": query, - "size": size, - "from": from_, - } - - if filters: - payload["filters"] = filters - if range_filters: - payload["range_filters"] = range_filters - if facets: - payload["facets"] = facets - if sort_by: - payload["sort_by"] = sort_by - payload["sort_order"] = sort_order - - try: - response = requests.post( - url, - json=payload, - timeout=self.timeout - ) - response.raise_for_status() - return response.json() - except requests.exceptions.RequestException as e: - raise Exception(f"搜索请求失败: {e}") - -# 使用示例 -client = SearchClient() -result = client.search( - query="玩具", - size=20, - filters={"category_keyword": "益智玩具"}, - range_filters={"min_price": {"gte": 50, "lte": 200}}, - facets=["category_keyword", "vendor_keyword"], - sort_by="min_price", - sort_order="asc" -) - -print(f"找到 {result['total']} 个结果") -for product in result['results']: - print(f"{product['title']} - ¥{product['price']}") -``` - -**完整的搜索函数(JavaScript)**: - -```javascript -class SearchClient { - constructor(baseUrl = 'http://localhost:6002') { - this.baseUrl = baseUrl; - this.timeout = 10000; - } - - async search({ - query, - size = 20, - from = 0, - filters = null, - rangeFilters = null, - facets = null, - sortBy = null, - sortOrder = 'desc' - }) { - const url = `${this.baseUrl}/search/`; - const payload = { - query, - size, - from, - }; - - if (filters) payload.filters = filters; - if (rangeFilters) payload.range_filters = rangeFilters; - if (facets) payload.facets = facets; - if (sortBy) { - payload.sort_by = sortBy; - payload.sort_order = sortOrder; - } - - try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(payload), - signal: AbortSignal.timeout(this.timeout) - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.error || `HTTP ${response.status}`); - } - - return await response.json(); - } catch (error) { - throw new Error(`搜索请求失败: ${error.message}`); - } - } -} - -// 使用示例 -const client = new SearchClient(); -const result = await client.search({ - query: '玩具', - size: 20, - filters: { category_keyword: '益智玩具' }, - rangeFilters: { min_price: { gte: 50, lte: 200 } }, - facets: ['category_keyword', 'vendor_keyword'], - sortBy: 'min_price', - sortOrder: 'asc' -}); - -console.log(`找到 ${result.total} 个结果`); -result.results.forEach(product => { - console.log(`${product.title} - ¥${product.price}`); -}); -``` - --- ## 其他接口 @@ -933,37 +720,6 @@ curl "http://localhost:6002/search/12345" --- -## 常见问题(FAQ) - -**Q1: 如何判断一个字段应该用哪种过滤器?** -`filters` 针对 keyword/布尔/整数字段做精确匹配;`range_filters` 针对数值或日期字段做区间查询。 - -**Q2: 可以同时使用多个过滤器吗?** -可以,所有过滤条件为 AND 关系。例如: - -```json -{ - "filters": { - "category_keyword": "玩具", - "vendor_keyword": "乐高" - }, - "range_filters": { - "min_price": {"gte": 50, "lte": 200} - } -} -``` - -**Q4: 分面结果里的 `selected` 字段含义是什么?** -指示该分面值是否已在当前过滤条件中,前端可据此高亮。 - -**Q5: 如何自定义排序?** -设置 `sort_by` 和 `sort_order`,常用字段包括 `min_price`, `max_price`, `title`, `create_time`, `update_time`, `relevance_score`。 - -**Q6: 如何启用调试模式?** -添加 `debug: true`,即可在响应中看到 `debug_info`(ES DSL、阶段耗时、打分细节)。 - ---- - ## 附录 ### 常用字段列表 -- libgit2 0.21.2