Commit b0ad8e894c2d3bd149ae8cdfd2ff02becfaf56c6

Authored by tangwang
1 parent ddc4abd1

文档完善

Showing 1 changed file with 28 additions and 272 deletions   Show diff stats
docs/搜索API对接指南.md
... ... @@ -35,31 +35,36 @@ curl -X POST "http://localhost:6002/search/" \
35 35 }'
36 36 ```
37 37  
38   -### Python示例
  38 +### curl示例:带过滤与分页
39 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']} 个结果")
  40 +```bash
  41 +curl -X POST "http://localhost:6002/search/" \
  42 + -H "Content-Type: application/json" \
  43 + -d '{
  44 + "tenant_id": "demo-tenant",
  45 + "query": "芭比娃娃 AND 配件",
  46 + "size": 5,
  47 + "from": 10,
  48 + "filters": {
  49 + "vendor_keyword": ["乐高", "孩之宝"]
  50 + },
  51 + "sort_by": "min_price",
  52 + "sort_order": "asc"
  53 + }'
47 54 ```
48 55  
49   -### JavaScript示例
  56 +### curl示例:开启调试与分面
50 57  
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} 个结果`);
  58 +```bash
  59 +curl -X POST "http://localhost:6002/search/" \
  60 + -H "Content-Type: application/json" \
  61 + -d '{
  62 + "tenant_id": "demo-tenant",
  63 + "query": "芭比娃娃",
  64 + "facets": ["category_keyword", "vendor_keyword"],
  65 + "min_score": 0.2,
  66 + "debug": true
  67 + }'
63 68 ```
64 69  
65 70 ---
... ... @@ -89,6 +94,7 @@ console.log(`找到 ${data.total} 个结果`);
89 94  
90 95 ```json
91 96 {
  97 + "tenant_id": "string (required)",
92 98 "query": "string (required)",
93 99 "size": 10,
94 100 "from": 0,
... ... @@ -108,6 +114,7 @@ console.log(`找到 ${data.total} 个结果`);
108 114  
109 115 | 参数 | 类型 | 必填 | 默认值 | 说明 |
110 116 |------|------|------|--------|------|
  117 +| `tenant_id` | string | ✅ | - | 租户ID,用于隔离不同站点或客户的数据 |
111 118 | `query` | string | ✅ | - | 搜索查询字符串,支持布尔表达式(AND, OR, RANK, ANDNOT) |
112 119 | `size` | integer | ❌ | 10 | 返回结果数量(1-100) |
113 120 | `from` | integer | ❌ | 0 | 分页偏移量(用于分页) |
... ... @@ -539,226 +546,6 @@ curl -X POST "http://localhost:6002/search/image" \
539 546  
540 547 ---
541 548  
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 549 ---
763 550  
764 551 ## 其他接口
... ... @@ -933,37 +720,6 @@ curl "http://localhost:6002/search/12345"
933 720  
934 721 ---
935 722  
936   -## 常见问题(FAQ)
937   -
938   -**Q1: 如何判断一个字段应该用哪种过滤器?**
939   -`filters` 针对 keyword/布尔/整数字段做精确匹配;`range_filters` 针对数值或日期字段做区间查询。
940   -
941   -**Q2: 可以同时使用多个过滤器吗?**
942   -可以,所有过滤条件为 AND 关系。例如:
943   -
944   -```json
945   -{
946   - "filters": {
947   - "category_keyword": "玩具",
948   - "vendor_keyword": "乐高"
949   - },
950   - "range_filters": {
951   - "min_price": {"gte": 50, "lte": 200}
952   - }
953   -}
954   -```
955   -
956   -**Q4: 分面结果里的 `selected` 字段含义是什么?**
957   -指示该分面值是否已在当前过滤条件中,前端可据此高亮。
958   -
959   -**Q5: 如何自定义排序?**
960   -设置 `sort_by` 和 `sort_order`,常用字段包括 `min_price`, `max_price`, `title`, `create_time`, `update_time`, `relevance_score`。
961   -
962   -**Q6: 如何启用调试模式?**
963   -添加 `debug: true`,即可在响应中看到 `debug_info`(ES DSL、阶段耗时、打分细节)。
964   -
965   ----
966   -
967 723 ## 附录
968 724  
969 725 ### 常用字段列表
... ...