Commit 4a677843805747d44b86f3e8394a9244a2ea4c19

Authored by tangwang
1 parent 670c701f

文档完善

API_DOCUMENTATION.md deleted
... ... @@ -1,938 +0,0 @@
1   -# 搜索引擎 API 接口文档
2   -
3   -## 概述
4   -
5   -本文档描述了电商搜索 SaaS 系统的 RESTful API 接口。系统提供强大的搜索功能,包括:
6   -
7   -- **多语言搜索**:支持中文、英文、俄文等多语言查询和自动翻译
8   -- **语义搜索**:基于 BGE-M3 文本向量和 CN-CLIP 图片向量的语义检索
9   -- **布尔表达式**:支持 AND、OR、RANK、ANDNOT 操作符
10   -- **灵活过滤**:精确匹配过滤器和数值范围过滤器
11   -- **分面搜索**:动态生成过滤选项,提供分组统计
12   -- **自定义排序**:支持按任意字段排序
13   -- **个性化排序**:可配置的相关性排序表达式
14   -
15   -## 基础信息
16   -
17   -- **Base URL**: `http://your-domain:6002` (http://120.76.41.98:6002)
18   -- **协议**: HTTP/HTTPS
19   -- **数据格式**: JSON
20   -- **字符编码**: UTF-8
21   -
22   -## 搜索接口
23   -
24   -### 1. 文本搜索
25   -
26   -**端点**: `POST /search/`
27   -
28   -**描述**: 执行文本搜索查询,支持多语言、布尔表达式、过滤器和分面搜索。
29   -
30   -#### 请求参数
31   -
32   -```json
33   -{
34   - "query": "string (required)",
35   - "size": 10,
36   - "from": 0,
37   - "filters": {},
38   - "range_filters": {},
39   - "facets": [],
40   - "sort_by": "string",
41   - "sort_order": "desc",
42   - "min_score": 0.0,
43   - "debug": false,
44   - "user_id": "string",
45   - "session_id": "string"
46   -}
47   -```
48   -
49   -#### 参数说明
50   -
51   -| 参数 | 类型 | 必填 | 默认值 | 描述 |
52   -|------|------|------|--------|------|
53   -| `query` | string | Y | - | 搜索查询字符串,支持布尔表达式(AND, OR, RANK, ANDNOT) |
54   -| `size` | integer | N | 10 | 返回结果数量(1-100) |
55   -| `from` | integer | N | 0 | 分页偏移量 |
56   -| `filters` | object | N | null | 精确匹配过滤器(见下文) |
57   -| `range_filters` | object | N | null | 数值范围过滤器(见下文) |
58   -| `facets` | array | N | null | 分面配置(见下文) |
59   -| `sort_by` | string | N | null | 排序字段名 |
60   -| `sort_order` | string | N | "desc" | 排序方向:`asc` 或 `desc` |
61   -| `min_score` | float | N | null | 最小相关性分数阈值 |
62   -| `debug` | boolean | N | false | 是否返回调试信息 |
63   -| `user_id` | string | N | null | 用户ID(用于个性化,预留) |
64   -| `session_id` | string | N | null | 会话ID(用于分析,预留) |
65   -
66   -#### 过滤器详解
67   -
68   -##### 精确匹配过滤器 (filters)
69   -
70   -用于精确匹配或多值匹配(OR 逻辑)。
71   -
72   -**格式**:
73   -```json
74   -{
75   - "filters": {
76   - "categoryName_keyword": "玩具", // 单值:精确匹配
77   - "brandName_keyword": ["乐高", "孩之宝"], // 数组:匹配任意值(OR)
78   - "in_stock": true // 布尔值
79   - }
80   -}
81   -```
82   -
83   -**支持的值类型**:
84   -- 字符串:精确匹配
85   -- 整数:精确匹配
86   -- 布尔值:精确匹配
87   -- 数组:匹配任意值(OR 逻辑)
88   -
89   -##### 范围过滤器 (range_filters)
90   -
91   -用于数值字段的范围过滤。
92   -
93   -**格式**:
94   -```json
95   -{
96   - "range_filters": {
97   - "price": {
98   - "gte": 50, // 大于等于
99   - "lte": 200 // 小于等于
100   - },
101   - "days_since_last_update": {
102   - "lte": 30 // 最近30天更新
103   - }
104   - }
105   -}
106   -```
107   -
108   -**支持的操作符**:
109   -- `gte`: 大于等于 (>=)
110   -- `gt`: 大于 (>)
111   -- `lte`: 小于等于 (<=)
112   -- `lt`: 小于 (<)
113   -
114   -**注意**: 至少需要指定一个操作符。
115   -
116   -##### 分面配置 (facets)
117   -
118   -用于生成分面统计(分组聚合)。
119   -
120   -**简单模式**(字符串数组):
121   -```json
122   -{
123   - "facets": ["categoryName_keyword", "brandName_keyword"]
124   -}
125   -```
126   -
127   -**高级模式**(配置对象数组):
128   -```json
129   -{
130   - "facets": [
131   - {
132   - "field": "categoryName_keyword",
133   - "size": 15,
134   - "type": "terms"
135   - },
136   - {
137   - "field": "price",
138   - "type": "range",
139   - "ranges": [
140   - {"key": "0-50", "to": 50},
141   - {"key": "50-100", "from": 50, "to": 100},
142   - {"key": "100-200", "from": 100, "to": 200},
143   - {"key": "200+", "from": 200}
144   - ]
145   - }
146   - ]
147   -}
148   -```
149   -
150   -**分面配置参数**:
151   -- `field`: 字段名(必填)
152   -- `size`: 返回的分组数量(默认:10,范围:1-100)
153   -- `type`: 分面类型,`terms`(分组统计)或 `range`(范围统计)
154   -- `ranges`: 范围定义(仅当 type='range' 时需要)
155   -
156   -#### 响应格式
157   -
158   -```json
159   -{
160   - "hits": [
161   - {
162   - "_id": "12345",
163   - "_score": 8.5,
164   - "_custom_score": 12.3,
165   - "_source": {
166   - "name": "芭比时尚娃娃",
167   - "price": 89.99,
168   - "categoryName": "玩具",
169   - "brandName": "美泰",
170   - "imageUrl": "https://example.com/image.jpg"
171   - }
172   - }
173   - ],
174   - "total": 118,
175   - "max_score": 8.5,
176   - "took_ms": 45,
177   - "facets": [
178   - {
179   - "field": "categoryName_keyword",
180   - "label": "商品类目",
181   - "type": "terms",
182   - "values": [
183   - {
184   - "value": "玩具",
185   - "label": "玩具",
186   - "count": 85,
187   - "selected": false
188   - },
189   - {
190   - "value": "益智玩具",
191   - "label": "益智玩具",
192   - "count": 33,
193   - "selected": false
194   - }
195   - ]
196   - }
197   - ],
198   - "query_info": {
199   - "original_query": "芭比娃娃",
200   - "detected_language": "zh",
201   - "translations": {
202   - "en": "barbie doll"
203   - }
204   - },
205   - "related_queries": null,
206   - "performance_info": {
207   - "total_duration": 45.2,
208   - "stage_durations": {
209   - "query_parsing": 5.3,
210   - "elasticsearch_search": 35.1,
211   - "result_processing": 4.8
212   - }
213   - },
214   - "debug_info": null
215   -}
216   -```
217   -
218   -#### 响应字段说明
219   -
220   -| 字段 | 类型 | 描述 |
221   -|------|------|------|
222   -| `hits` | array | 搜索结果列表 |
223   -| `hits[]._id` | string | 文档ID |
224   -| `hits[]._score` | float | 相关性分数 |
225   -| `hits[]._custom_score` | float | 自定义排序分数(如启用) |
226   -| `hits[]._source` | object | 文档内容 |
227   -| `total` | integer | 匹配的总文档数 |
228   -| `max_score` | float | 最高相关性分数 |
229   -| `took_ms` | integer | 搜索耗时(毫秒) |
230   -| `facets` | array | 分面统计结果(标准化格式) |
231   -| `facets[].field` | string | 字段名 |
232   -| `facets[].label` | string | 显示标签 |
233   -| `facets[].type` | string | 分面类型:`terms` 或 `range` |
234   -| `facets[].values` | array | 分面值列表 |
235   -| `facets[].values[].value` | any | 分面值 |
236   -| `facets[].values[].label` | string | 显示标签 |
237   -| `facets[].values[].count` | integer | 文档数量 |
238   -| `facets[].values[].selected` | boolean | 是否已选中 |
239   -| `query_info` | object | 查询处理信息 |
240   -| `related_queries` | array | 相关搜索(预留) |
241   -| `performance_info` | object | 性能信息 |
242   -| `debug_info` | object | 调试信息(仅当 debug=true) |
243   -
244   -#### 请求示例
245   -
246   -**示例 1: 简单搜索**
247   -
248   -```bash
249   -curl -X POST "http://localhost:6002/search/" \
250   - -H "Content-Type: application/json" \
251   - -d '{
252   - "query": "芭比娃娃",
253   - "size": 20
254   - }'
255   -```
256   -
257   -**示例 2: 带过滤器的搜索**
258   -
259   -```bash
260   -curl -X POST "http://localhost:6002/search/" \
261   - -H "Content-Type: application/json" \
262   - -d '{
263   - "query": "玩具",
264   - "size": 20,
265   - "filters": {
266   - "categoryName_keyword": ["玩具", "益智玩具"],
267   - "in_stock": true
268   - },
269   - "range_filters": {
270   - "price": {
271   - "gte": 50,
272   - "lte": 200
273   - }
274   - }
275   - }'
276   -```
277   -
278   -**示例 3: 带分面搜索(简单模式)**
279   -
280   -```bash
281   -curl -X POST "http://localhost:6002/search/" \
282   - -H "Content-Type: application/json" \
283   - -d '{
284   - "query": "玩具",
285   - "size": 20,
286   - "facets": ["categoryName_keyword", "brandName_keyword"]
287   - }'
288   -```
289   -
290   -**示例 4: 带分面搜索(高级模式)**
291   -
292   -```bash
293   -curl -X POST "http://localhost:6002/search/" \
294   - -H "Content-Type: application/json" \
295   - -d '{
296   - "query": "玩具",
297   - "size": 20,
298   - "facets": [
299   - {
300   - "field": "categoryName_keyword",
301   - "size": 15,
302   - "type": "terms"
303   - },
304   - {
305   - "field": "brandName_keyword",
306   - "size": 15,
307   - "type": "terms"
308   - },
309   - {
310   - "field": "price",
311   - "type": "range",
312   - "ranges": [
313   - {"key": "0-50", "to": 50},
314   - {"key": "50-100", "from": 50, "to": 100},
315   - {"key": "100-200", "from": 100, "to": 200},
316   - {"key": "200+", "from": 200}
317   - ]
318   - }
319   - ]
320   - }'
321   -```
322   -
323   -**示例 5: 复杂搜索(布尔表达式+过滤+排序)**
324   -
325   -```bash
326   -curl -X POST "http://localhost:6002/search/" \
327   - -H "Content-Type: application/json" \
328   - -d '{
329   - "query": "玩具 AND (乐高 OR 芭比)",
330   - "size": 20,
331   - "filters": {
332   - "categoryName_keyword": "玩具"
333   - },
334   - "range_filters": {
335   - "price": {
336   - "gte": 50,
337   - "lte": 200
338   - },
339   - "days_since_last_update": {
340   - "lte": 30
341   - }
342   - },
343   - "facets": [
344   - {"field": "brandName_keyword", "size": 15},
345   - {"field": "supplierName_keyword", "size": 10}
346   - ],
347   - "sort_by": "min_price",
348   - "sort_order": "asc",
349   - "debug": false
350   - }'
351   -```
352   -
353   ----
354   -
355   -### 2. 图片搜索
356   -
357   -**端点**: `POST /search/image`
358   -
359   -**描述**: 基于图片相似度进行搜索,使用图片向量进行语义匹配。
360   -
361   -#### 请求参数
362   -
363   -```json
364   -{
365   - "image_url": "string (required)",
366   - "size": 10,
367   - "filters": {},
368   - "range_filters": {}
369   -}
370   -```
371   -
372   -#### 参数说明
373   -
374   -| 参数 | 类型 | 必填 | 默认值 | 描述 |
375   -|------|------|------|--------|------|
376   -| `image_url` | string | ✅ | - | 查询图片的 URL |
377   -| `size` | integer | ❌ | 10 | 返回结果数量(1-100) |
378   -| `filters` | object | ❌ | null | 精确匹配过滤器 |
379   -| `range_filters` | object | ❌ | null | 数值范围过滤器 |
380   -
381   -#### 响应格式
382   -
383   -与文本搜索相同,但 `query_info` 包含图片信息:
384   -
385   -```json
386   -{
387   - "hits": [...],
388   - "total": 50,
389   - "max_score": 0.95,
390   - "took_ms": 120,
391   - "query_info": {
392   - "image_url": "https://example.com/image.jpg",
393   - "search_type": "image_similarity"
394   - }
395   -}
396   -```
397   -
398   -#### 请求示例
399   -
400   -```bash
401   -curl -X POST "http://localhost:6002/search/image" \
402   - -H "Content-Type: application/json" \
403   - -d '{
404   - "image_url": "https://example.com/barbie.jpg",
405   - "size": 20,
406   - "filters": {
407   - "categoryName_keyword": "玩具"
408   - },
409   - "range_filters": {
410   - "price": {
411   - "lte": 100
412   - }
413   - }
414   - }'
415   -```
416   -
417   ----
418   -
419   -### 3. 搜索建议(框架)
420   -
421   -**端点**: `GET /search/suggestions`
422   -
423   -**描述**: 获取搜索建议(自动补全)。
424   -
425   -**注意**: 此功能暂未实现,仅返回框架响应。
426   -
427   -#### 查询参数
428   -
429   -| 参数 | 类型 | 必填 | 默认值 | 描述 |
430   -|------|------|------|--------|------|
431   -| `q` | string | ✅ | - | 搜索查询字符串(最少1个字符) |
432   -| `size` | integer | ❌ | 5 | 建议数量(1-20) |
433   -| `types` | string | ❌ | "query" | 建议类型(逗号分隔):query, product, category, brand |
434   -
435   -#### 响应格式
436   -
437   -```json
438   -{
439   - "query": "芭",
440   - "suggestions": [
441   - {
442   - "text": "芭比娃娃",
443   - "type": "query",
444   - "highlight": "<em>芭</em>比娃娃",
445   - "popularity": 850
446   - }
447   - ],
448   - "took_ms": 5
449   -}
450   -```
451   -
452   -#### 请求示例
453   -
454   -```bash
455   -curl "http://localhost:6002/search/suggestions?q=芭&size=5&types=query,product"
456   -```
457   -
458   ----
459   -
460   -### 4. 即时搜索(框架)
461   -
462   -**端点**: `GET /search/instant`
463   -
464   -**描述**: 即时搜索,边输入边搜索。
465   -
466   -**注意**: 此功能暂未实现,调用标准搜索接口。
467   -
468   -#### 查询参数
469   -
470   -| 参数 | 类型 | 必填 | 默认值 | 描述 |
471   -|------|------|------|--------|------|
472   -| `q` | string | ✅ | - | 搜索查询(最少2个字符) |
473   -| `size` | integer | ❌ | 5 | 结果数量(1-20) |
474   -
475   -#### 请求示例
476   -
477   -```bash
478   -curl "http://localhost:6002/search/instant?q=玩具&size=5"
479   -```
480   -
481   ----
482   -
483   -### 5. 获取单个文档
484   -
485   -**端点**: `GET /search/{doc_id}`
486   -
487   -**描述**: 根据文档ID获取单个文档详情。
488   -
489   -#### 路径参数
490   -
491   -| 参数 | 类型 | 描述 |
492   -|------|------|------|
493   -| `doc_id` | string | 文档ID |
494   -
495   -#### 响应格式
496   -
497   -```json
498   -{
499   - "id": "12345",
500   - "source": {
501   - "name": "芭比时尚娃娃",
502   - "price": 89.99,
503   - "categoryName": "玩具"
504   - }
505   -}
506   -```
507   -
508   -#### 请求示例
509   -
510   -```bash
511   -curl "http://localhost:6002/search/12345"
512   -```
513   -
514   ----
515   -
516   -## 管理接口
517   -
518   -### 1. 健康检查
519   -
520   -**端点**: `GET /admin/health`
521   -
522   -**描述**: 检查服务健康状态。
523   -
524   -#### 响应格式
525   -
526   -```json
527   -{
528   - "status": "healthy",
529   - "elasticsearch": "connected",
530   - "tenant_id": "tenant1"
531   -}
532   -```
533   -
534   ----
535   -
536   -### 2. 获取配置
537   -
538   -**端点**: `GET /admin/config`
539   -
540   -**描述**: 获取当前客户配置(脱敏)。
541   -
542   -#### 响应格式
543   -
544   -```json
545   -{
546   - "tenant_id": "tenant1",
547   - "tenant_name": "Tenant1 Test Instance",
548   - "es_index_name": "search_tenant1",
549   - "num_fields": 20,
550   - "num_indexes": 4,
551   - "supported_languages": ["zh", "en", "ru"],
552   - "ranking_expression": "bm25() + 0.2*text_embedding_relevance()",
553   - "spu_enabled": false
554   -}
555   -```
556   -
557   ----
558   -
559   -### 3. 索引统计
560   -
561   -**端点**: `GET /admin/stats`
562   -
563   -**描述**: 获取索引统计信息。
564   -
565   -#### 响应格式
566   -
567   -```json
568   -{
569   - "index_name": "search_tenant1",
570   - "document_count": 10000,
571   - "size_mb": 523.45
572   -}
573   -```
574   -
575   ----
576   -
577   -### 4. 查询改写规则
578   -
579   -**端点**: `GET /admin/rewrite-rules`
580   -
581   -**描述**: 获取当前的查询改写规则。
582   -
583   -#### 响应格式
584   -
585   -```json
586   -{
587   - "rules": {
588   - "乐高": "brand:乐高 OR name:乐高",
589   - "玩具": "category:玩具"
590   - },
591   - "count": 2
592   -}
593   -```
594   -
595   -**端点**: `POST /admin/rewrite-rules`
596   -
597   -**描述**: 更新查询改写规则。
598   -
599   -#### 请求格式
600   -
601   -```json
602   -{
603   - "乐高": "brand:乐高 OR name:乐高",
604   - "芭比": "brand:芭比 OR name:芭比"
605   -}
606   -```
607   -
608   ----
609   -
610   -## 使用示例
611   -
612   -### Python 示例
613   -
614   -```python
615   -import requests
616   -
617   -API_URL = "http://localhost:6002/search/"
618   -
619   -# 简单搜索
620   -response = requests.post(API_URL, json={
621   - "query": "芭比娃娃",
622   - "size": 20
623   -})
624   -data = response.json()
625   -print(f"找到 {data['total']} 个结果")
626   -
627   -# 带过滤器和分面的搜索
628   -response = requests.post(API_URL, json={
629   - "query": "玩具",
630   - "size": 20,
631   - "filters": {
632   - "categoryName_keyword": ["玩具", "益智玩具"]
633   - },
634   - "range_filters": {
635   - "price": {"gte": 50, "lte": 200}
636   - },
637   - "facets": [
638   - {"field": "brandName_keyword", "size": 15},
639   - {"field": "categoryName_keyword", "size": 15}
640   - ],
641   - "sort_by": "min_price",
642   - "sort_order": "asc"
643   -})
644   -result = response.json()
645   -
646   -# 处理分面结果
647   -for facet in result.get('facets', []):
648   - print(f"\n{facet['label']}:")
649   - for value in facet['values']:
650   - print(f" - {value['label']}: {value['count']}")
651   -```
652   -
653   -### JavaScript 示例
654   -
655   -```javascript
656   -// 搜索函数
657   -async function searchProducts(query, filters, rangeFilters, facets) {
658   - const response = await fetch('http://localhost:6002/search/', {
659   - method: 'POST',
660   - headers: {
661   - 'Content-Type': 'application/json'
662   - },
663   - body: JSON.stringify({
664   - query: query,
665   - size: 20,
666   - filters: filters,
667   - range_filters: rangeFilters,
668   - facets: facets
669   - })
670   - });
671   -
672   - const data = await response.json();
673   - return data;
674   -}
675   -
676   -// 使用示例
677   -const result = await searchProducts(
678   - "玩具",
679   - { categoryName_keyword: ["玩具"] },
680   - { price: { gte: 50, lte: 200 } },
681   - [
682   - { field: "brandName_keyword", size: 15 },
683   - { field: "categoryName_keyword", size: 15 }
684   - ]
685   -);
686   -
687   -// 显示分面结果
688   -result.facets.forEach(facet => {
689   - console.log(`${facet.label}:`);
690   - facet.values.forEach(value => {
691   - console.log(` - ${value.label}: ${value.count}`);
692   - });
693   -});
694   -
695   -// 显示搜索结果
696   -result.hits.forEach(hit => {
697   - const product = hit._source;
698   - console.log(`${product.name} - ¥${product.price}`);
699   -});
700   -```
701   -
702   -### cURL 示例
703   -
704   -```bash
705   -# 简单搜索
706   -curl -X POST "http://localhost:6002/search/" \
707   - -H "Content-Type: application/json" \
708   - -d '{"query": "芭比娃娃", "size": 20}'
709   -
710   -# 带过滤和排序
711   -curl -X POST "http://localhost:6002/search/" \
712   - -H "Content-Type: application/json" \
713   - -d '{
714   - "query": "玩具",
715   - "size": 20,
716   - "filters": {"categoryName_keyword": "玩具"},
717   - "range_filters": {"price": {"gte": 50, "lte": 200}},
718   - "sort_by": "min_price",
719   - "sort_order": "asc"
720   - }'
721   -
722   -# 带分面搜索
723   -curl -X POST "http://localhost:6002/search/" \
724   - -H "Content-Type: application/json" \
725   - -d '{
726   - "query": "玩具",
727   - "size": 20,
728   - "facets": [
729   - {"field": "categoryName_keyword", "size": 15},
730   - {"field": "brandName_keyword", "size": 15}
731   - ]
732   - }'
733   -```
734   -
735   ----
736   -
737   -## 布尔表达式语法
738   -
739   -### 支持的操作符
740   -
741   -| 操作符 | 描述 | 示例 |
742   -|--------|------|------|
743   -| `AND` | 所有词必须匹配 | `玩具 AND 乐高` |
744   -| `OR` | 任意词匹配 | `芭比 OR 娃娃` |
745   -| `ANDNOT` | 排除特定词 | `玩具 ANDNOT 电动` |
746   -| `RANK` | 排序加权(不强制匹配) | `玩具 RANK 乐高` |
747   -| `()` | 分组 | `玩具 AND (乐高 OR 芭比)` |
748   -
749   -### 操作符优先级
750   -
751   -从高到低:
752   -1. `()` - 括号
753   -2. `ANDNOT` - 排除
754   -3. `AND` - 与
755   -4. `OR` - 或
756   -5. `RANK` - 排序
757   -
758   -### 查询示例
759   -
760   -```
761   -# 简单查询
762   -"芭比娃娃"
763   -
764   -# AND 查询
765   -"玩具 AND 乐高"
766   -
767   -# OR 查询
768   -"芭比 OR 娃娃"
769   -
770   -# 排除查询
771   -"玩具 ANDNOT 电动"
772   -
773   -# 复杂查询
774   -"玩具 AND (乐高 OR 芭比) ANDNOT 电动"
775   -
776   -# 域查询
777   -"brand:乐高"
778   -"category:玩具"
779   -"title:芭比娃娃"
780   -```
781   -
782   ----
783   -
784   -## 数据模型
785   -
786   -### 商品字段
787   -
788   -常见的商品字段包括:
789   -
790   -| 字段名 | 类型 | 描述 |
791   -|--------|------|------|
792   -| `skuId` | long | SKU ID(主键) |
793   -| `name` | text | 商品名称(中文) |
794   -| `enSpuName` | text | 商品名称(英文) |
795   -| `ruSkuName` | text | 商品名称(俄文) |
796   -| `categoryName` | text | 类目名称 |
797   -| `categoryName_keyword` | keyword | 类目名称(精确匹配) |
798   -| `brandName` | text | 品牌名称 |
799   -| `brandName_keyword` | keyword | 品牌名称(精确匹配) |
800   -| `supplierName` | text | 供应商名称 |
801   -| `supplierName_keyword` | keyword | 供应商名称(精确匹配) |
802   -| `price` | double | 价格 |
803   -| `imageUrl` | keyword | 商品图片URL |
804   -| `create_time` | date | 创建时间 |
805   -| `days_since_last_update` | int | 距上次更新天数 |
806   -
807   -**注意**: 不同客户可能有不同的字段配置。
808   -
809   ----
810   -
811   -
812   -## 常见问题
813   -
814   -### Q1: 如何判断一个字段应该用哪种过滤器?
815   -
816   -**A**:
817   -- **精确匹配过滤器** (`filters`): 用于 KEYWORD 类型字段(如类目、品牌、标签等)
818   -- **范围过滤器** (`range_filters`): 用于数值类型字段(如价格、库存、时间等)
819   -
820   -### Q2: 可以同时使用多个过滤器吗?
821   -
822   -**A**: 可以。多个过滤器之间是 AND 关系(必须同时满足)。
823   -
824   -```json
825   -{
826   - "filters": {
827   - "categoryName_keyword": "玩具",
828   - "brandName_keyword": "乐高"
829   - },
830   - "range_filters": {
831   - "price": {"gte": 50, "lte": 200}
832   - }
833   -}
834   -```
835   -结果:类目是"玩具" **并且** 品牌是"乐高" **并且** 价格在50-200之间。
836   -
837   -### Q3: 如何实现"价格小于50或大于200"的过滤?
838   -
839   -**A**: 当前版本不支持单字段多个不连续范围。建议分两次查询或使用布尔查询。
840   -
841   -### Q4: 分面搜索返回的 selected 字段是什么意思?
842   -
843   -**A**: `selected` 表示该分面值是否在当前的过滤器中。前端可以用它来高亮已选中的过滤项。
844   -
845   -### Q5: 如何使用自定义排序?
846   -
847   -**A**: 使用 `sort_by` 和 `sort_order` 参数:
848   -
849   -```json
850   -{
851   - "query": "玩具",
852   - "sort_by": "price",
853   - "sort_order": "asc" // 价格从低到高
854   -}
855   -```
856   -
857   -常用排序字段:
858   -- `price`: 价格
859   -- `create_time`: 创建时间
860   -- `days_since_last_update`: 更新时间
861   -
862   -### Q6: 如何启用调试模式?
863   -
864   -**A**: 设置 `debug: true`,响应中会包含 `debug_info` 字段,包含:
865   -- 查询分析过程
866   -- ES 查询 DSL
867   -- ES 响应详情
868   -- 各阶段耗时
869   -
870   ----
871   -
872   -## 版本历史
873   -
874   -### v3.0 (2024-11-12)
875   -
876   -**重大更新**:
877   -- ✅ 移除硬编码的 `price_ranges` 逻辑
878   -- ✅ 新增 `range_filters` 参数,支持任意数值字段的范围过滤
879   -- ✅ 新增 `facets` 参数,替代 `aggregations`,提供简化接口
880   -- ✅ 标准化分面搜索响应格式
881   -- ✅ 新增 `/search/suggestions` 端点(框架)
882   -- ✅ 新增 `/search/instant` 端点(框架)
883   -- ❌ **移除** `aggregations` 参数(不向后兼容)
884   -
885   -**迁移指南**:
886   -
887   -旧接口:
888   -```json
889   -{
890   - "filters": {
891   - "price_ranges": ["0-50", "50-100"]
892   - },
893   - "aggregations": {
894   - "category_stats": {"terms": {"field": "categoryName_keyword", "size": 15}}
895   - }
896   -}
897   -```
898   -
899   -新接口:
900   -```json
901   -{
902   - "range_filters": {
903   - "price": {"gte": 50, "lte": 100}
904   - },
905   - "facets": [
906   - {"field": "categoryName_keyword", "size": 15}
907   - ]
908   -}
909   -```
910   -
911   ----
912   -
913   -## 附录
914   -
915   -### A. 支持的分析器
916   -
917   -| 分析器 | 语言 | 描述 |
918   -|--------|------|------|
919   -| `chinese_ecommerce` | 中文 | Ansj 中文分词器(电商优化) |
920   -| `english` | 英文 | 标准英文分析器 |
921   -| `russian` | 俄文 | 俄文分析器 |
922   -| `arabic` | 阿拉伯文 | 阿拉伯文分析器 |
923   -| `spanish` | 西班牙文 | 西班牙文分析器 |
924   -| `japanese` | 日文 | 日文分析器 |
925   -
926   -### B. 字段类型
927   -
928   -| 类型 | ES 映射 | 用途 |
929   -|------|---------|------|
930   -| `TEXT` | text | 全文检索 |
931   -| `KEYWORD` | keyword | 精确匹配、聚合、排序 |
932   -| `LONG` | long | 整数 |
933   -| `DOUBLE` | double | 浮点数 |
934   -| `DATE` | date | 日期时间 |
935   -| `BOOLEAN` | boolean | 布尔值 |
936   -| `TEXT_EMBEDDING` | dense_vector | 文本向量(1024维) |
937   -| `IMAGE_EMBEDDING` | dense_vector | 图片向量(1024维) |
938   -
BEST_PRACTICES_REFACTORING.md deleted
... ... @@ -1,274 +0,0 @@
1   -# 最佳实践重构总结
2   -
3   -**重构日期**: 2025-11-12
4   -**重构人员**: AI Assistant
5   -**重构原则**: 代码简洁、类型安全、单一职责
6   -
7   ----
8   -
9   -## 重构目标
10   -
11   -1. **移除兼容代码**:不再支持多种数据格式,统一使用最佳实践
12   -2. **修复前端422错误**:支持日期时间字符串的 range filter
13   -3. **保持代码简洁**:单一数据流,清晰的类型定义
14   -
15   ----
16   -
17   -## 修改文件
18   -
19   -### 1. `/home/tw/SearchEngine/api/models.py`
20   -
21   -#### 修改:RangeFilter 模型
22   -
23   -**之前**:只支持数值 (float)
24   -```python
25   -gte: Optional[float] = Field(None, description="大于等于 (>=)")
26   -```
27   -
28   -**之后**:支持数值和日期时间字符串
29   -```python
30   -gte: Optional[Union[float, str]] = Field(None, description="大于等于 (>=)。数值或ISO日期时间字符串")
31   -```
32   -
33   -**理由**:
34   -- 价格字段需要数值过滤:`{"gte": 50, "lte": 200}`
35   -- 时间字段需要字符串过滤:`{"gte": "2023-01-01T00:00:00Z"}`
36   -- 使用 `Union[float, str]` 同时支持两种类型
37   -
38   ----
39   -
40   -### 2. `/home/tw/SearchEngine/search/es_query_builder.py`
41   -
42   -#### 修改:build_facets 方法
43   -
44   -**之前**:兼容字典和 Pydantic 模型
45   -```python
46   -else:
47   - if isinstance(config, dict):
48   - field = config['field']
49   - facet_type = config.get('type', 'terms')
50   - ...
51   - else:
52   - # Pydantic模型
53   - field = config.field
54   - facet_type = config.type
55   - ...
56   -```
57   -
58   -**之后**:只支持标准格式(str 或 FacetConfig)
59   -```python
60   -# 简单模式:只有字段名(字符串)
61   -if isinstance(config, str):
62   - field = config
63   - ...
64   -
65   -# 高级模式:FacetConfig 对象
66   -else:
67   - field = config.field
68   - facet_type = config.type
69   - ...
70   -```
71   -
72   -**理由**:
73   -- API 层已经定义标准格式:`List[Union[str, FacetConfig]]`
74   -- 移除字典支持,保持单一数据流
75   -- 代码更简洁,意图更清晰
76   -
77   ----
78   -
79   -### 3. `/home/tw/SearchEngine/search/searcher.py`
80   -
81   -#### 修改:_standardize_facets 方法
82   -
83   -**之前**:兼容字典和 Pydantic 模型
84   -```python
85   -else:
86   - field = config.get('field') if isinstance(config, dict) else config.field
87   - facet_type = config.get('type', 'terms') if isinstance(config, dict) else getattr(config, 'type', 'terms')
88   -```
89   -
90   -**之后**:只支持标准格式
91   -```python
92   -else:
93   - # FacetConfig 对象
94   - field = config.field
95   - facet_type = config.type
96   -```
97   -
98   -**理由**:
99   -- 与 build_facets 保持一致
100   -- 移除冗余的类型检查
101   -- 代码更清晰易读
102   -
103   ----
104   -
105   -## 数据流设计
106   -
107   -### 标准数据流(最佳实践)
108   -
109   -```
110   -API Request
111   - ↓
112   -Pydantic 验证 (SearchRequest)
113   - ↓ facets: List[Union[str, FacetConfig]]
114   -Searcher.search()
115   - ↓
116   -QueryBuilder.build_facets()
117   - ↓ 只接受 str 或 FacetConfig
118   -ES Query (aggs)
119   - ↓
120   -ES Response (aggregations)
121   - ↓
122   -Searcher._standardize_facets()
123   - ↓ 只处理 str 或 FacetConfig
124   -List[FacetResult] (Pydantic 模型)
125   - ↓
126   -SearchResponse
127   - ↓
128   -API Response (JSON)
129   -```
130   -
131   -### 类型定义
132   -
133   -```python
134   -# API 层
135   -facets: Optional[List[Union[str, FacetConfig]]] = None
136   -
137   -# Query Builder 层
138   -def build_facets(facet_configs: Optional[List[Union[str, 'FacetConfig']]])
139   -
140   -# Searcher 层
141   -def _standardize_facets(...) -> Optional[List[FacetResult]]
142   -
143   -# Response 层
144   -facets: Optional[List[FacetResult]] = None
145   -```
146   -
147   ----
148   -
149   -## 测试结果
150   -
151   -### ✅ 所有测试通过
152   -
153   -| 测试场景 | 状态 | 说明 |
154   -|---------|------|------|
155   -| 字符串 facets | ✓ | `["categoryName_keyword"]` |
156   -| FacetConfig 对象 | ✓ | `{"field": "price", "type": "range", ...}` |
157   -| 数值 range filter | ✓ | `{"gte": 50, "lte": 200}` |
158   -| 日期时间 range filter | ✓ | `{"gte": "2023-01-01T00:00:00Z"}` |
159   -| 混合使用 | ✓ | filters + range_filters + facets |
160   -
161   -### ✅ 前端 422 错误已修复
162   -
163   -**问题**:前端筛选 listing time 时返回 422 Unprocessable Entity
164   -
165   -**原因**:`RangeFilter` 只接受 `float`,不接受日期时间字符串
166   -
167   -**解决**:修改 `RangeFilter` 支持 `Union[float, str]`
168   -
169   -**验证**:
170   -```bash
171   -curl -X POST /search/ \
172   - -d '{"query": "玩具", "range_filters": {"create_time": {"gte": "2023-01-01T00:00:00Z"}}}'
173   -# ✓ 200 OK
174   -```
175   -
176   ----
177   -
178   -## 代码质量
179   -
180   -### ✅ 无 Linter 错误
181   -```bash
182   -No linter errors found.
183   -```
184   -
185   -### ✅ 类型安全
186   -- 所有类型明确定义
187   -- Pydantic 自动验证
188   -- IDE 类型提示完整
189   -
190   -### ✅ 代码简洁
191   -- 移除所有兼容代码
192   -- 单一数据格式
193   -- 清晰的控制流
194   -
195   ----
196   -
197   -## 最佳实践原则
198   -
199   -### 1. **单一职责**
200   -每个方法只处理一种标准格式,不兼容多种输入
201   -
202   -### 2. **类型明确**
203   -使用 Pydantic 模型而不是字典,类型在编译时就确定
204   -
205   -### 3. **数据流清晰**
206   -API → Pydantic → 业务逻辑 → Pydantic → Response
207   -
208   -### 4. **早期验证**
209   -在 API 层就验证数据,不在内部做多重兼容检查
210   -
211   -### 5. **代码可维护**
212   -- 删除冗余代码
213   -- 保持一致性
214   -- 易于理解和修改
215   -
216   ----
217   -
218   -## 对比总结
219   -
220   -| 方面 | 重构前 | 重构后 |
221   -|------|-------|--------|
222   -| **数据格式** | 字典 + Pydantic(兼容) | 只有 Pydantic |
223   -| **类型检查** | 运行时多重检查 | 编译时类型明确 |
224   -| **代码行数** | 更多(兼容代码) | 更少(单一逻辑) |
225   -| **可维护性** | 复杂(多种路径) | 简单(单一路径) |
226   -| **错误处理** | 隐式容错 | 明确验证 |
227   -| **RangeFilter** | 只支持数值 | 支持数值+字符串 |
228   -| **前端兼容** | 422 错误 | 完全兼容 |
229   -
230   ----
231   -
232   -## 后续建议
233   -
234   -### 1. 代码审查
235   -- [x] 移除所有字典兼容代码
236   -- [x] 统一使用 Pydantic 模型
237   -- [x] 修复前端 422 错误
238   -
239   -### 2. 测试覆盖
240   -- [x] 字符串 facets
241   -- [x] FacetConfig 对象
242   -- [x] 数值 range filter
243   -- [x] 日期时间 range filter
244   -- [x] 混合场景
245   -
246   -### 3. 文档更新
247   -- [x] 最佳实践文档
248   -- [x] 数据流设计
249   -- [ ] API 文档更新
250   -
251   -### 4. 性能优化
252   -- [ ] 添加请求缓存
253   -- [ ] 优化 Pydantic 验证
254   -- [ ] 监控性能指标
255   -
256   ----
257   -
258   -## 结论
259   -
260   -本次重构成功实现了以下目标:
261   -
262   -✅ **代码简洁**:移除所有兼容代码,保持单一数据流
263   -✅ **类型安全**:统一使用 Pydantic 模型,编译时类型检查
264   -✅ **功能完整**:修复前端 422 错误,支持所有筛选场景
265   -✅ **可维护性**:代码清晰,易于理解和修改
266   -
267   -**核心原则**:*不做兼容多种方式的代码,定义一种最佳实践,所有模块都适配这种新方式*
268   -
269   ----
270   -
271   -**版本**: v3.1
272   -**状态**: ✅ 完成并通过测试
273   -**下次更新**: 根据业务需求扩展
274   -
CHANGES.md deleted
... ... @@ -1,611 +0,0 @@
1   -# 变更日志 (CHANGELOG)
2   -
3   ----
4   -
5   -## v3.0 - API 接口重构 (2024-11-12)
6   -
7   -### 重大更新
8   -
9   -本版本对搜索 API 进行了全面重构,移除硬编码逻辑,实现了灵活通用的 SaaS 接口设计。
10   -
11   -#### ❌ 破坏性变更(不向后兼容)
12   -
13   -1. **移除硬编码的 price_ranges 参数**
14   - - 旧方式:`filters: {"price_ranges": ["0-50", "50-100"]}`
15   - - 新方式:`range_filters: {"price": {"gte": 50, "lte": 100}}`
16   -
17   -2. **移除 aggregations 参数**
18   - - 旧方式:`aggregations: {"category_stats": {"terms": {...}}}`(ES DSL)
19   - - 新方式:`facets: [{"field": "categoryName_keyword", "size": 15}]`(简化配置)
20   -
21   -3. **响应格式变更**
22   - - 旧方式:`response.aggregations`(ES 原始格式)
23   - - 新方式:`response.facets`(标准化格式)
24   -
25   -#### ✅ 新增功能
26   -
27   -1. **结构化过滤参数**
28   - - 新增 `range_filters` 参数:支持任意数值字段的范围过滤
29   - - 支持 `gte`, `gt`, `lte`, `lt` 操作符
30   - - 示例:
31   - ```json
32   - {
33   - "range_filters": {
34   - "price": {"gte": 50, "lte": 200},
35   - "days_since_last_update": {"lte": 30}
36   - }
37   - }
38   - ```
39   -
40   -2. **简化的分面配置**
41   - - 新增 `facets` 参数:替代复杂的 ES DSL
42   - - 支持简单模式(字符串数组)和高级模式(配置对象)
43   - - 示例:
44   - ```json
45   - {
46   - "facets": [
47   - "categoryName_keyword", // 简单模式
48   - {"field": "brandName_keyword", "size": 15} // 高级模式
49   - ]
50   - }
51   - ```
52   -
53   -3. **标准化分面响应**
54   - - 统一的分面结果格式
55   - - 包含 `field`, `label`, `type`, `values`
56   - - `values` 包含 `value`, `label`, `count`, `selected`
57   - - 示例:
58   - ```json
59   - {
60   - "facets": [
61   - {
62   - "field": "categoryName_keyword",
63   - "label": "商品类目",
64   - "type": "terms",
65   - "values": [
66   - {"value": "玩具", "label": "玩具", "count": 85, "selected": false}
67   - ]
68   - }
69   - ]
70   - }
71   - ```
72   -
73   -4. **新增搜索建议端点**(框架)
74   - - `GET /search/suggestions`: 自动补全
75   - - `GET /search/instant`: 即时搜索
76   - - 注:暂未实现,仅返回框架响应
77   -
78   -#### 🔧 代码改进
79   -
80   -1. **后端模型层** (`api/models.py`)
81   - - 新增 `RangeFilter` 模型
82   - - 新增 `FacetConfig` 模型
83   - - 新增 `FacetValue` 和 `FacetResult` 模型
84   - - 更新 `SearchRequest` 和 `SearchResponse`
85   -
86   -2. **查询构建器** (`search/es_query_builder.py`)
87   - - **完全移除** 硬编码的 `price_ranges` 逻辑(第 205-233 行)
88   - - 重构 `_build_filters` 方法,支持 `range_filters`
89   - - **删除** `add_dynamic_aggregations` 方法
90   - - 新增 `build_facets` 方法
91   -
92   -3. **搜索执行层** (`search/searcher.py`)
93   - - 更新 `search()` 方法签名
94   - - 新增 `_standardize_facets()` 方法
95   - - 新增 `_get_field_label()` 方法
96   - - 更新 `SearchResult` 类
97   -
98   -4. **API 路由层** (`api/routes/search.py`)
99   - - 更新所有搜索端点
100   - - 新增 `/search/suggestions` 端点
101   - - 新增 `/search/instant` 端点
102   -
103   -5. **前端代码** (`frontend/static/js/app.js`)
104   - - 更新状态管理,添加 `rangeFilters`
105   - - 完全重写 `displayAggregations` 为 `displayFacets`
106   - - 更新过滤器处理逻辑
107   - - 删除所有硬编码的 `price_ranges`
108   -
109   -#### 📚 文档更新
110   -
111   -- 新增 `API_DOCUMENTATION.md`:完整的 API 接口文档
112   -- 更新 `README.md`:添加 v3.0 新功能说明
113   -- 更新 `USER_GUIDE.md`:更新 API 使用示例
114   -
115   -#### 🎯 改进要点
116   -
117   -**从特定实现到通用 SaaS**:
118   -- ❌ 移除:硬编码的价格范围值
119   -- ❌ 移除:暴露 ES DSL 的聚合接口
120   -- ❌ 移除:不统一的响应格式
121   -- ✅ 新增:通用的范围过滤器
122   -- ✅ 新增:简化的分面配置
123   -- ✅ 新增:标准化的响应格式
124   -
125   -#### 📋 迁移指南
126   -
127   -**旧接口** → **新接口**
128   -
129   -1. **过滤器迁移**:
130   - ```json
131   - // 旧
132   - {"filters": {"price_ranges": ["50-100"]}}
133   -
134   - // 新
135   - {"range_filters": {"price": {"gte": 50, "lte": 100}}}
136   - ```
137   -
138   -2. **聚合迁移**:
139   - ```json
140   - // 旧
141   - {
142   - "aggregations": {
143   - "category_stats": {
144   - "terms": {"field": "categoryName_keyword", "size": 15}
145   - }
146   - }
147   - }
148   -
149   - // 新
150   - {
151   - "facets": [
152   - {"field": "categoryName_keyword", "size": 15}
153   - ]
154   - }
155   - ```
156   -
157   -3. **响应解析迁移**:
158   - ```javascript
159   - // 旧
160   - data.aggregations.category_stats.buckets.forEach(bucket => {
161   - console.log(bucket.key, bucket.doc_count);
162   - });
163   -
164   - // 新
165   - data.facets.forEach(facet => {
166   - facet.values.forEach(value => {
167   - console.log(value.value, value.count);
168   - });
169   - });
170   - ```
171   -
172   ----
173   -
174   -## v2.x - 前端优化更新 (2025-11-11)
175   -
176   -## 概述
177   -基于提供的电商搜索引擎参考图片,对前端界面进行了全面重新设计和优化,采用更现代、简洁的布局风格。
178   -
179   ----
180   -
181   -## 修改的文件
182   -
183   -### 1. `/home/tw/SearchEngine/frontend/index.html` ✅ 完全重写
184   -**更改内容:**
185   -- 去除旧的搜索示例和复杂布局
186   -- 添加简洁的顶部标题栏(Product + 商品数量 + Fold按钮)
187   -- 重新设计搜索栏(更简洁)
188   -- 添加水平筛选标签区域(Categories, Brand, Supplier)
189   -- 添加排序工具栏(带上下箭头的排序按钮)
190   -- 改用网格布局展示商品
191   -- 添加分页组件
192   -- 将查询信息改为可折叠的Debug区域
193   -
194   -**关键改进:**
195   -```html
196   -<!-- 新增顶部标题栏 -->
197   -<header class="top-header">
198   - <div class="header-left">
199   - <span class="logo">Product</span>
200   - <span class="product-count">0 products found</span>
201   - </div>
202   - <div class="header-right">
203   - <button class="fold-btn">Fold</button>
204   - </div>
205   -</header>
206   -
207   -<!-- 新增水平筛选标签 -->
208   -<div class="filter-section">
209   - <div class="filter-row">
210   - <div class="filter-label">Categories:</div>
211   - <div class="filter-tags" id="categoryTags"></div>
212   - </div>
213   - <!-- 品牌、供应商等 -->
214   -</div>
215   -
216   -<!-- 新增排序栏(带箭头) -->
217   -<div class="sort-section">
218   - <button class="sort-btn">
219   - By Price
220   - <span class="sort-arrows">
221   - <span class="arrow-up">▲</span>
222   - <span class="arrow-down">▼</span>
223   - </span>
224   - </button>
225   -</div>
226   -
227   -<!-- 商品网格 -->
228   -<div class="product-grid"></div>
229   -
230   -<!-- 分页 -->
231   -<div class="pagination"></div>
232   -```
233   -
234   ----
235   -
236   -### 2. `/home/tw/SearchEngine/frontend/static/css/style.css` ✅ 完全重写
237   -**更改内容:**
238   -- 去除紫色渐变背景,改为白色简洁背景
239   -- 重新设计所有组件样式
240   -- 添加顶部标题栏样式
241   -- 添加水平筛选标签样式(带hover和active状态)
242   -- 添加排序按钮样式(带箭头)
243   -- 重新设计商品卡片样式(网格布局)
244   -- 添加分页样式
245   -- 优化响应式设计
246   -
247   -**关键样式:**
248   -```css
249   -/* 白色背景 */
250   -body {
251   - background: #f5f5f5;
252   -}
253   -
254   -/* 筛选标签 */
255   -.filter-tag {
256   - padding: 6px 15px;
257   - background: #f8f8f8;
258   - border: 1px solid #ddd;
259   - cursor: pointer;
260   -}
261   -
262   -.filter-tag.active {
263   - background: #e74c3c;
264   - color: white;
265   -}
266   -
267   -/* 排序箭头 */
268   -.sort-arrows {
269   - display: inline-flex;
270   - flex-direction: column;
271   - font-size: 10px;
272   -}
273   -
274   -/* 商品网格 */
275   -.product-grid {
276   - display: grid;
277   - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
278   - gap: 20px;
279   -}
280   -
281   -/* 商品卡片 */
282   -.product-card {
283   - background: white;
284   - border: 1px solid #e0e0e0;
285   - border-radius: 8px;
286   - transition: all 0.3s;
287   -}
288   -
289   -.product-card:hover {
290   - box-shadow: 0 4px 12px rgba(0,0,0,0.1);
291   - transform: translateY(-2px);
292   -}
293   -```
294   -
295   -**代码量对比:**
296   -- 旧版:433行
297   -- 新版:450行
298   -- 变化:+17行(增加了更多功能和响应式样式)
299   -
300   ----
301   -
302   -### 3. `/home/tw/SearchEngine/frontend/static/js/app.js` ✅ 完全重构
303   -**更改内容:**
304   -- 添加状态管理对象(统一管理所有状态)
305   -- 重写搜索函数(支持分页)
306   -- 重写结果展示函数(商品网格布局)
307   -- 重写筛选聚合函数(水平标签展示)
308   -- 添加排序函数(支持字段+方向)
309   -- 添加分页函数(完整分页导航)
310   -- 优化代码结构(更模块化)
311   -
312   -**关键功能:**
313   -```javascript
314   -// 状态管理
315   -let state = {
316   - query: '',
317   - currentPage: 1,
318   - pageSize: 20,
319   - totalResults: 0,
320   - filters: {},
321   - sortBy: '',
322   - sortOrder: 'desc',
323   - aggregations: null
324   -};
325   -
326   -// 排序函数(支持上下箭头)
327   -function sortByField(field, order) {
328   - state.sortBy = field;
329   - state.sortOrder = order;
330   - performSearch(state.currentPage);
331   -}
332   -
333   -// 分页函数
334   -function goToPage(page) {
335   - performSearch(page);
336   - window.scrollTo({ top: 0, behavior: 'smooth' });
337   -}
338   -
339   -// 商品网格展示
340   -function displayResults(data) {
341   - // 生成商品卡片HTML
342   - data.hits.forEach((hit) => {
343   - html += `
344   - <div class="product-card">
345   - <div class="product-image-wrapper">...</div>
346   - <div class="product-price">...</div>
347   - <div class="product-moq">...</div>
348   - <div class="product-title">...</div>
349   - </div>
350   - `;
351   - });
352   -}
353   -
354   -// 水平筛选标签
355   -function displayAggregations(aggregations) {
356   - // 显示为可点击的标签
357   - html += `
358   - <span class="filter-tag ${isActive ? 'active' : ''}"
359   - onclick="toggleFilter(...)">
360   - ${key} (${count})
361   - </span>
362   - `;
363   -}
364   -```
365   -
366   -**代码量对比:**
367   -- 旧版:516行
368   -- 新版:465行
369   -- 变化:-51行(代码更简洁,功能更强)
370   -
371   ----
372   -
373   -### 4. `/home/tw/SearchEngine/api/app.py` ✅ 添加静态文件服务
374   -**更改内容:**
375   -- 导入 `FileResponse` 和 `StaticFiles`
376   -- 添加前端HTML服务路由
377   -- 挂载静态文件目录(CSS, JS)
378   -- 将原有的 `/` 路由改为 `/api`
379   -
380   -**关键代码:**
381   -```python
382   -from fastapi.responses import FileResponse
383   -from fastapi.staticfiles import StaticFiles
384   -
385   -# 在文件末尾添加
386   -frontend_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "frontend")
387   -if os.path.exists(frontend_path):
388   - # 服务前端HTML
389   - @app.get("/")
390   - async def serve_frontend():
391   - index_path = os.path.join(frontend_path, "index.html")
392   - if os.path.exists(index_path):
393   - return FileResponse(index_path)
394   -
395   - # 挂载静态文件
396   - app.mount("/static", StaticFiles(directory=os.path.join(frontend_path, "static")), name="static")
397   -```
398   -
399   ----
400   -
401   -## 新增的文件
402   -
403   -### 5. `/home/tw/SearchEngine/frontend/README.md` ✅ 新建
404   -前端详细文档,包含:
405   -- 优化说明
406   -- 功能介绍
407   -- 使用方法
408   -- 技术特点
409   -- 浏览器兼容性
410   -- 未来改进计划
411   -
412   -### 6. `/home/tw/SearchEngine/FRONTEND_GUIDE.md` ✅ 新建
413   -快速上手指南,包含:
414   -- 优化总结
415   -- 启动方法
416   -- 测试步骤
417   -- 常见问题
418   -- API接口说明
419   -- 性能指标
420   -
421   -### 7. `/home/tw/SearchEngine/scripts/test_frontend.sh` ✅ 新建
422   -自动化测试脚本,测试:
423   -- 健康检查
424   -- 前端HTML
425   -- CSS文件
426   -- JavaScript文件
427   -- 搜索API
428   -
429   -### 8. `/home/tw/SearchEngine/CHANGES.md` ✅ 新建
430   -本文件,记录所有更改。
431   -
432   ----
433   -
434   -## 功能对比表
435   -
436   -| 功能 | 旧版前端 | 新版前端 | 状态 |
437   -|------|---------|---------|------|
438   -| 背景颜色 | 紫色渐变 | 白色简洁 | ✅ 优化 |
439   -| 顶部标题栏 | 大标题+副标题 | Product + 商品数 | ✅ 优化 |
440   -| 搜索框 | 带多个选项 | 简洁搜索框 | ✅ 优化 |
441   -| 筛选方式 | 左侧垂直面板 | 顶部水平标签 | ✅ 优化 |
442   -| 筛选交互 | 复选框 | 可点击标签 | ✅ 优化 |
443   -| 排序方式 | 下拉选择 | 按钮+箭头 | ✅ 优化 |
444   -| 商品展示 | 列表布局 | 网格布局 | ✅ 优化 |
445   -| 商品卡片 | 横向卡片 | 垂直卡片 | ✅ 优化 |
446   -| 分页功能 | ❌ 无 | ✅ 完整分页 | ✅ 新增 |
447   -| 响应式设计 | 基础支持 | 完整响应式 | ✅ 优化 |
448   -| 代码结构 | 混乱 | 模块化 | ✅ 优化 |
449   -| 状态管理 | 分散 | 统一管理 | ✅ 优化 |
450   -
451   ----
452   -
453   -## 技术改进
454   -
455   -### 前端架构
456   -- ✅ **状态管理**:统一的state对象
457   -- ✅ **模块化**:功能清晰分离
458   -- ✅ **代码简化**:去除冗余代码
459   -- ✅ **性能优化**:减少DOM操作
460   -
461   -### UI/UX设计
462   -- ✅ **视觉一致性**:统一的设计语言
463   -- ✅ **交互直观**:标签式筛选,箭头排序
464   -- ✅ **响应迅速**:即时反馈
465   -- ✅ **移动友好**:完整的响应式支持
466   -
467   -### 代码质量
468   -- ✅ **可维护性**:清晰的结构
469   -- ✅ **可扩展性**:易于添加新功能
470   -- ✅ **可读性**:注释完整
471   -- ✅ **无linter错误**:代码规范
472   -
473   ----
474   -
475   -## 测试步骤
476   -
477   -### 1. 启动服务
478   -```bash
479   -cd /home/tw/SearchEngine
480   -bash scripts/start_backend.sh
481   -```
482   -
483   -### 2. 运行测试
484   -```bash
485   -bash scripts/test_frontend.sh
486   -```
487   -
488   -### 3. 手动测试
489   -访问:`http://120.76.41.98:6002/`
490   -
491   -测试项目:
492   -- [ ] 页面正常加载
493   -- [ ] 搜索功能正常
494   -- [ ] 筛选标签可点击
495   -- [ ] 排序箭头可用
496   -- [ ] 商品网格展示正常
497   -- [ ] 分页功能正常
498   -- [ ] 响应式布局正常
499   -
500   ----
501   -
502   -## 兼容性
503   -
504   -### 浏览器
505   -- ✅ Chrome 90+
506   -- ✅ Firefox 88+
507   -- ✅ Safari 14+
508   -- ✅ Edge 90+
509   -- ✅ 移动浏览器
510   -
511   -### 屏幕尺寸
512   -- ✅ 桌面(1920x1080)
513   -- ✅ 笔记本(1366x768)
514   -- ✅ 平板(768x1024)
515   -- ✅ 手机(375x667)
516   -
517   ----
518   -
519   -## 性能指标
520   -
521   -| 指标 | 旧版 | 新版 | 改进 |
522   -|------|------|------|------|
523   -| 首屏加载 | ~1.5s | ~0.8s | ⬇️ 47% |
524   -| JavaScript大小 | 15KB | 13KB | ⬇️ 13% |
525   -| CSS大小 | 12KB | 11KB | ⬇️ 8% |
526   -| DOM节点数 | ~350 | ~200 | ⬇️ 43% |
527   -| 重绘次数 | 高 | 低 | ⬆️ 优化 |
528   -
529   ----
530   -
531   -## 最佳实践应用
532   -
533   -### HTML
534   -- ✅ 语义化标签
535   -- ✅ 无障碍支持(ARIA)
536   -- ✅ SEO友好
537   -
538   -### CSS
539   -- ✅ CSS Grid布局
540   -- ✅ Flexbox布局
541   -- ✅ CSS变量
542   -- ✅ 媒体查询(响应式)
543   -
544   -### JavaScript
545   -- ✅ ES6+语法
546   -- ✅ 事件委托
547   -- ✅ 防抖/节流(如需要)
548   -- ✅ 错误处理
549   -
550   ----
551   -
552   -## 下一步优化建议
553   -
554   -### 短期(1-2周)
555   -- [ ] 添加加载骨架屏
556   -- [ ] 优化图片懒加载
557   -- [ ] 添加搜索建议(自动完成)
558   -
559   -### 中期(1个月)
560   -- [ ] 添加用户偏好设置
561   -- [ ] 支持多主题切换
562   -- [ ] 添加商品收藏功能
563   -
564   -### 长期(3个月)
565   -- [ ] PWA支持(离线访问)
566   -- [ ] 国际化(多语言)
567   -- [ ] 性能监控
568   -
569   ----
570   -
571   -## 回滚方案
572   -
573   -如需回滚到旧版:
574   -
575   -```bash
576   -cd /home/tw/SearchEngine
577   -git checkout HEAD~1 frontend/
578   -# 或从备份恢复
579   -```
580   -
581   ----
582   -
583   -## 总结
584   -
585   -### 完成情况
586   -- ✅ HTML重构:100%
587   -- ✅ CSS重写:100%
588   -- ✅ JavaScript重构:100%
589   -- ✅ 后端适配:100%
590   -- ✅ 文档编写:100%
591   -- ✅ 测试脚本:100%
592   -
593   -### 核心成果
594   -1. **更好的用户体验**:简洁、直观的界面
595   -2. **更强的功能**:完整的筛选、排序、分页
596   -3. **更好的代码**:模块化、可维护
597   -4. **更好的性能**:更快的加载和响应
598   -
599   -### 达成目标
600   -✅ 完全符合参考图片的布局风格
601   -✅ 实现了所有要求的功能
602   -✅ 遵循了最佳实践
603   -✅ 代码质量高,易于维护
604   -✅ 响应式设计,支持多端
605   -
606   ----
607   -
608   -**优化完成时间**:2025-11-11
609   -**总耗时**:约2小时
610   -**状态**:✅ 生产就绪
611   -
README.md
... ... @@ -15,12 +15,12 @@
15 15  
16 16 | 步骤 | 去哪里看 | 摘要 |
17 17 |------|---------|------|
18   -| 1. 准备环境 | `环境相关.md` / `USAGE_GUIDE.md` | Conda/依赖、Elasticsearch、MySQL、必需的变量 |
19   -| 2. 构造测试数据 | `TEST_DATA_GUIDE.md` | Tenant1 Mock、Tenant2 CSV、`mock_data.sh` / `ingest.sh` |
20   -| 3. 启动与验证 | `USAGE_GUIDE.md` | `run.sh` 一键启动、分步脚本、日志与健康检查 |
21   -| 4. 理解架构 | `设计文档.md` | 数据流、配置系统、查询/搜索/索引模块 |
22   -| 5. 接入搜索 API | `API_DOCUMENTATION.md` / `API_INTEGRATION_GUIDE.md` | REST 端点、参数、响应、最佳实践 |
23   -| 6. 查字段定义 | `INDEX_FIELDS_DOCUMENTATION.md` | `search_products` 映射、字段来源、类型与用途 |
  18 +| 1. 准备环境 | `环境配置说明.md` / `Usage-Guide.md` | Conda/依赖、Elasticsearch、MySQL、必需变量 |
  19 +| 2. 构造测试数据 | `测试数据指南.md` | Tenant1 Mock、Tenant2 CSV、`mock_data.sh` / `ingest.sh` |
  20 +| 3. 启动与验证 | `Usage-Guide.md` | `run.sh` 一键启动、分步脚本、日志与健康检查 |
  21 +| 4. 理解架构 | `系统设计文档.md` | 数据流、配置系统、查询/搜索/索引模块 |
  22 +| 5. 接入搜索 API | `搜索API对接指南.md` / `搜索API速查表.md` | REST 端点、参数、响应、最佳实践 |
  23 +| 6. 查字段定义 | `索引字段说明.md` | `search_products` 映射、字段来源、类型与用途 |
24 24  
25 25 > README 仅保留最常用命令的“索引”。细节以主题文档为准。
26 26  
... ... @@ -52,19 +52,21 @@ curl -X POST http://localhost:6002/search/ \
52 52  
53 53 | 文档 | 内容提要 | 适用场景 |
54 54 |------|----------|----------|
55   -| `环境相关.md` | 系统要求、Conda/依赖、外部服务账号、常用端口 | 首次部署、环境核对 |
56   -| `USAGE_GUIDE.md` | 环境准备、服务启动、配置、日志、验证手册 | 日常运维、调试 |
57   -| `TEST_DATA_GUIDE.md` | 两个租户的模拟/CSV数据构造 & MySQL→ES流程 | 数据准备、联调 |
58   -| `设计文档.md` | 架构、配置系统、索引/查询/排序模块细节 | 研发/扩展功能 |
59   -| `INDEX_FIELDS_DOCUMENTATION.md` | `search_products` 字段、类型、来源、嵌套结构 | 新增字段、数据对齐 |
60   -| `API_DOCUMENTATION.md` | REST API(搜索/图片/管理)详解、示例、响应格式 | API 使用、测试 |
61   -| `API_INTEGRATION_GUIDE.md` | 客户对接指引、最佳实践、错误处理、语言说明 | 第三方集成、SDK 开发 |
62   -| `API_QUICK_REFERENCE.md` | 常用请求体速查表 | 支持团队快速查阅 |
63   -| `环境相关.md` + `.env` 模板 | 运行依赖账号、端口、密钥对照表 | 交付 & 运维 |
  55 +| `环境配置说明.md` | 系统要求、Conda/依赖、外部服务账号、常用端口 | 首次部署、环境核对 |
  56 +| `Usage-Guide.md` | 环境准备、服务启动、配置、日志、验证手册 | 日常运维、调试 |
  57 +| `基础配置指南.md` | 租户字段、索引域、排序表达式配置流程 | 新租户开通、配置变更 |
  58 +| `测试数据指南.md` | 两个租户的模拟/CSV 数据构造 & MySQL→ES 流程 | 数据准备、联调 |
  59 +| `测试Pipeline说明.md` | 测试流水线、CI 脚本、上下文说明 | 自动化测试、追踪流水线 |
  60 +| `系统设计文档.md` | 架构、配置系统、索引/查询/排序模块细节 | 研发/扩展功能 |
  61 +| `索引字段说明.md` | `search_products` 字段、类型、来源、嵌套结构 | 新增字段、数据对齐 |
  62 +| `搜索API对接指南.md` | REST API(文本/图片/管理)详解、示例、响应格式 | API 使用、测试 |
  63 +| `搜索API速查表.md` | 常用请求体、过滤器、分面速查表 | 支持团队快速查阅 |
  64 +| `Search-API-Examples.md` | Python/JS/cURL 端到端示例 | 客户工程、SDK 参考 |
  65 +| `环境配置说明.md` + `.env` 模板 | 运行依赖账号、端口、密钥对照表 | 交付 & 运维 |
64 66  
65 67 更多补充材料:
66 68  
67   -- `TEST_DATA_GUIDE.md`:包含完整工作流脚本示例
  69 +- `测试数据指南.md`:包含完整工作流脚本示例
68 70 - `商品数据源入ES配置规范.md`:数据源映射约定
69 71 - `MULTILANG_FEATURE.md`:多语言处理细节
70 72  
... ... @@ -73,12 +75,12 @@ curl -X POST http://localhost:6002/search/ \
73 75 - **数据构建 → MySQL → Elasticsearch**
74 76 - `scripts/mock_data.sh`:Tenant1 Mock + Tenant2 CSV 一条龙
75 77 - `scripts/ingest.sh <tenant_id> [recreate]`:驱动 `indexer/` 模块写入 `search_products`
76   - - 详解:`TEST_DATA_GUIDE.md`
  78 + - 详解:`测试数据指南.md`
77 79  
78 80 - **搜索服务 & API**
79 81 - `api/`(FastAPI)承载 REST API,`search/` + `query/` 负责查询解析与下发
80   - - API、分页、过滤、Facet、KNN 等:`API_DOCUMENTATION.md`
81   - - 对接案例与错误码:`API_INTEGRATION_GUIDE.md`
  82 + - API、分页、过滤、Facet、KNN 等:`搜索API对接指南.md`
  83 + - 对接案例、示例与错误码:`搜索API对接指南.md`、`Search-API-Examples.md`
82 84  
83 85 - **配置驱动能力**
84 86 - `config/schema/{tenant_id}/config.yaml`:字段定义、索引域、排序表达式、SPU 聚合
... ... @@ -96,16 +98,3 @@ scripts/ 数据/服务脚本(mock_data, ingest, run 等)
96 98 frontend/ 简易调试页面
97 99 docs/ 运营及中文资料
98 100 ```
99   -
100   -## 常用参考
101   -
102   -- **运行/排障**:`USAGE_GUIDE.md`、`环境相关.md`
103   -- **功能设计**:`设计文档.md`
104   -- **字段/数据对齐**:`INDEX_FIELDS_DOCUMENTATION.md`
105   -- **API 对接**:`API_DOCUMENTATION.md`、`API_INTEGRATION_GUIDE.md`
106   -- **测试数据**:`TEST_DATA_GUIDE.md`
107   -
108   -## 许可证
109   -
110   -专有软件 - 保留所有权利
111   -
... ...
docs/RequestContext_README.md deleted
... ... @@ -1,374 +0,0 @@
1   -# RequestContext 使用指南
2   -
3   -## 概述
4   -
5   -`RequestContext` 是一个请求粒度的上下文管理器,用于跟踪和管理搜索请求的整个生命周期。它提供了统一的数据存储、性能监控和日志记录功能。
6   -
7   -## 核心功能
8   -
9   -### 1. 查询分析结果存储
10   -- 原始查询、规范化查询、重写查询
11   -- 检测语言和翻译结果
12   -- 查询向量(embedding)
13   -- 布尔查询AST
14   -
15   -### 2. 各检索阶段中间结果
16   -- 解析后的查询对象
17   -- 布尔查询语法树
18   -- ES查询DSL
19   -- ES响应数据
20   -- 处理后的搜索结果
21   -
22   -### 3. 性能监控
23   -- 自动计时各阶段耗时
24   -- 计算各阶段耗时占比
25   -- 识别性能瓶颈
26   -- 详细的性能摘要日志
27   -
28   -### 4. 错误处理和警告
29   -- 统一的错误信息存储
30   -- 警告信息收集
31   -- 完整的上下文错误跟踪
32   -
33   -## 支持的搜索阶段
34   -
35   -```python
36   -class RequestContextStage(Enum):
37   - TOTAL = "total_search" # 总搜索时间
38   - QUERY_PARSING = "query_parsing" # 查询解析
39   - BOOLEAN_PARSING = "boolean_parsing" # 布尔查询解析
40   - QUERY_BUILDING = "query_building" # ES查询构建
41   - ELASTICSEARCH_SEARCH = "elasticsearch_search" # ES搜索
42   - RESULT_PROCESSING = "result_processing" # 结果处理
43   - RERANKING = "reranking" # 重排序
44   -```
45   -
46   -## 基本使用方法
47   -
48   -### 1. 创建RequestContext
49   -
50   -```python
51   -from context import create_request_context, RequestContext
52   -
53   -# 方式1: 使用工厂函数
54   -context = create_request_context(reqid="req-001", uid="user-123")
55   -
56   -# 方式2: 直接创建
57   -context = RequestContext(reqid="req-001", uid="user-123")
58   -
59   -# 方式3: 作为上下文管理器使用
60   -with create_request_context("req-002", "user-456") as context:
61   - # 搜索逻辑
62   - pass # 自动记录性能摘要
63   -```
64   -
65   -### 2. 阶段计时
66   -
67   -```python
68   -from context import RequestContextStage
69   -
70   -# 开始计时
71   -context.start_stage(RequestContextStage.QUERY_PARSING)
72   -
73   -# 执行查询解析逻辑
74   -# parsed_query = query_parser.parse(query, context=context)
75   -
76   -# 结束计时
77   -duration = context.end_stage(RequestContextStage.QUERY_PARSING)
78   -print(f"查询解析耗时: {duration:.2f}ms")
79   -```
80   -
81   -### 3. 存储查询分析结果
82   -
83   -```python
84   -context.store_query_analysis(
85   - original_query="红色连衣裙",
86   - normalized_query="红色 连衣裙",
87   - rewritten_query="红色 女 连衣裙",
88   - detected_language="zh",
89   - translations={"en": "red dress"},
90   - query_vector=[0.1, 0.2, 0.3, ...], # 如果有向量
91   - is_simple_query=True
92   -)
93   -```
94   -
95   -### 4. 存储中间结果
96   -
97   -```python
98   -# 存储解析后的查询对象
99   -context.store_intermediate_result('parsed_query', parsed_query)
100   -
101   -# 存储ES查询DSL
102   -context.store_intermediate_result('es_query', es_query_dict)
103   -
104   -# 存储ES响应
105   -context.store_intermediate_result('es_response', es_response)
106   -
107   -# 存储处理后的结果
108   -context.store_intermediate_result('processed_hits', hits)
109   -```
110   -
111   -### 5. 错误处理和警告
112   -
113   -```python
114   -try:
115   - # 可能出错的操作
116   - risky_operation()
117   -except Exception as e:
118   - context.set_error(e)
119   -
120   -# 添加警告信息
121   -context.add_warning("查询结果较少,建议放宽搜索条件")
122   -
123   -# 检查是否有错误
124   -if context.has_error():
125   - print(f"搜索出错: {context.metadata['error_info']}")
126   -```
127   -
128   -## 在Searcher中使用
129   -
130   -### 1. 自动创建Context(向后兼容)
131   -
132   -```python
133   -searcher = Searcher(config, es_client)
134   -
135   -# Searcher会自动创建RequestContext
136   -result = searcher.search(
137   - query="无线蓝牙耳机",
138   - size=10,
139   - enable_embedding=True
140   -)
141   -
142   -# 结果中包含context信息
143   -print(result.context.get_summary())
144   -```
145   -
146   -### 2. 手动创建和传递Context
147   -
148   -```python
149   -# 创建自己的context
150   -context = create_request_context("my-req-001", "user-789")
151   -
152   -# 传递给searcher
153   -result = searcher.search(
154   - query="运动鞋",
155   - context=context # 传递自定义context
156   -)
157   -
158   -# 使用context进行详细分析
159   -summary = context.get_summary()
160   -print(f"总耗时: {summary['performance']['total_duration_ms']:.1f}ms")
161   -```
162   -
163   -## 性能分析
164   -
165   -### 1. 获取性能摘要
166   -
167   -```python
168   -summary = context.get_summary()
169   -
170   -# 基本信息
171   -print(f"请求ID: {summary['request_info']['reqid']}")
172   -print(f"总耗时: {summary['performance']['total_duration_ms']:.1f}ms")
173   -
174   -# 各阶段耗时
175   -for stage, duration in summary['performance']['stage_timings_ms'].items():
176   - percentage = summary['performance']['stage_percentages'].get(stage, 0)
177   - print(f"{stage}: {duration:.1f}ms ({percentage:.1f}%)")
178   -
179   -# 查询分析信息
180   -query_info = summary['query_analysis']
181   -print(f"原查询: {query_info['original_query']}")
182   -print(f"重写查询: {query_info['rewritten_query']}")
183   -print(f"检测语言: {query_info['detected_language']}")
184   -```
185   -
186   -### 2. 识别性能瓶颈
187   -
188   -```python
189   -summary = context.get_summary()
190   -
191   -# 找出耗时超过20%的阶段
192   -bottlenecks = []
193   -for stage, percentage in summary['performance']['stage_percentages'].items():
194   - if percentage > 20:
195   - bottlenecks.append((stage, percentage))
196   -
197   -if bottlenecks:
198   - print("性能瓶颈:")
199   - for stage, percentage in bottlenecks:
200   - print(f" - {stage}: {percentage:.1f}%")
201   -```
202   -
203   -### 3. 自动性能日志
204   -
205   -RequestContext会在以下时机自动记录详细的性能摘要日志:
206   -
207   -- 上下文管理器退出时 (`with context:`)
208   -- 手动调用 `context.log_performance_summary()`
209   -- Searcher.search() 完成时
210   -
211   -日志格式示例:
212   -```
213   -[2024-01-01 10:30:45] [INFO] [request_context] 搜索请求性能摘要 | reqid: req-001 | 总耗时: 272.6ms | 阶段耗时: | - query_parsing: 35.3ms (13.0%) | - elasticsearch_search: 146.0ms (53.6%) | - result_processing: 18.6ms (6.8%) | 查询: '红色连衣裙' -> '红色 女 连衣裙' (zh) | 结果: 156 hits ES查询: 2456 chars
214   -```
215   -
216   -## 线程安全
217   -
218   -RequestContext是线程安全的,支持并发请求处理。每个请求使用独立的context实例,互不干扰。
219   -
220   -```python
221   -import threading
222   -from context import create_request_context
223   -
224   -def worker(request_id, query):
225   - context = create_request_context(request_id)
226   - # 搜索逻辑
227   - # context自动跟踪此线程的请求
228   - pass
229   -
230   -# 多线程并发处理
231   -threads = []
232   -for i in range(5):
233   - t = threading.Thread(target=worker, args=(f"req-{i}", f"query-{i}"))
234   - threads.append(t)
235   - t.start()
236   -
237   -for t in threads:
238   - t.join()
239   -```
240   -
241   -## 调试支持
242   -
243   -### 1. 检查中间结果
244   -
245   -```python
246   -# 获取查询解析结果
247   -parsed_query = context.get_intermediate_result('parsed_query')
248   -
249   -# 获取ES查询DSL
250   -es_query = context.get_intermediate_result('es_query')
251   -
252   -# 获取ES响应
253   -es_response = context.get_intermediate_result('es_response')
254   -
255   -# 获取原始搜索结果
256   -raw_hits = context.get_intermediate_result('raw_hits')
257   -
258   -# 获取最终处理后的结果
259   -processed_hits = context.get_intermediate_result('processed_hits')
260   -```
261   -
262   -### 2. 错误诊断
263   -
264   -```python
265   -if context.has_error():
266   - error_info = context.metadata['error_info']
267   - print(f"错误类型: {error_info['type']}")
268   - print(f"错误消息: {error_info['message']}")
269   -
270   - # 检查是否有警告
271   - if context.metadata['warnings']:
272   - print("警告信息:")
273   - for warning in context.metadata['warnings']:
274   - print(f" - {warning}")
275   -```
276   -
277   -## 最佳实践
278   -
279   -### 1. 统一使用Context
280   -
281   -```python
282   -# 推荐:在整个搜索流程中传递同一个context
283   -result = searcher.search(query, context=context)
284   -
285   -# 不推荐:在各个环节创建不同的context
286   -```
287   -
288   -### 2. 合理设置阶段边界
289   -
290   -```python
291   -# 只在有意义的大阶段之间计时
292   -context.start_stage(RequestContextStage.QUERY_PARSING)
293   -# 整个查询解析逻辑
294   -context.end_stage(RequestContextStage.QUERY_PARSING)
295   -
296   -# 避免在细粒度操作间频繁计时
297   -```
298   -
299   -### 3. 及时存储关键数据
300   -
301   -```python
302   -# 在每个阶段完成后及时存储结果
303   -context.store_intermediate_result('parsed_query', parsed_query)
304   -context.store_intermediate_result('es_query', es_query)
305   -
306   -# 便于后续调试和分析
307   -```
308   -
309   -### 4. 适当使用警告
310   -
311   -```python
312   -# 使用警告记录非致命问题
313   -if total_hits < 10:
314   - context.add_warning("搜索结果较少,建议放宽搜索条件")
315   -
316   -if query_time > 5.0:
317   - context.add_warning(f"查询耗时较长: {query_time:.1f}秒")
318   -```
319   -
320   -## 集成示例
321   -
322   -### API接口集成
323   -
324   -```python
325   -from flask import Flask, request, jsonify
326   -from context import create_request_context
327   -
328   -app = Flask(__name__)
329   -
330   -@app.route('/search')
331   -def api_search():
332   - # 从请求中获取参数
333   - query = request.args.get('q', '')
334   - uid = request.args.get('uid', 'anonymous')
335   -
336   - # 创建context
337   - context = create_request_context(uid=uid)
338   -
339   - try:
340   - # 执行搜索
341   - result = searcher.search(query, context=context)
342   -
343   - # 返回结果(包含性能信息)
344   - response = {
345   - 'results': result.to_dict(),
346   - 'performance': context.get_summary()['performance']
347   - }
348   -
349   - return jsonify(response)
350   -
351   - except Exception as e:
352   - context.set_error(e)
353   - context.log_performance_summary()
354   -
355   - return jsonify({
356   - 'error': str(e),
357   - 'request_id': context.reqid
358   - }), 500
359   -```
360   -
361   -## 总结
362   -
363   -RequestContext提供了一个强大而灵活的框架,用于管理搜索请求的整个生命周期。通过统一的上下文管理、自动性能监控和详细的日志记录,它显著提升了搜索系统的可观测性和调试能力。
364   -
365   -主要优势:
366   -
367   -1. **统一管理**: 所有请求相关数据集中存储
368   -2. **自动监控**: 无需手动计时,自动跟踪性能
369   -3. **详细日志**: 完整的请求生命周期记录
370   -4. **向后兼容**: 现有代码无需修改即可受益
371   -5. **线程安全**: 支持高并发场景
372   -6. **易于调试**: 丰富的中间结果和错误信息
373   -
374   -通过合理使用RequestContext,可以构建更加可靠、高性能和易维护的搜索系统。
375 0 \ No newline at end of file
API_EXAMPLES.md renamed to docs/Search-API-Examples.md
USAGE_GUIDE.md renamed to docs/Usage-Guide.md
商品数据源入ES配置规范.md renamed to docs/reference/商品数据源入ES配置规范.md
阿里opensearch电商行业.md renamed to docs/reference/阿里opensearch电商行业.md
docs/BASE_CONFIG_GUIDE.md renamed to docs/基础配置指南.md
API_INTEGRATION_GUIDE.md renamed to docs/搜索API对接指南.md
... ... @@ -761,6 +761,209 @@ result.results.forEach(product =&gt; {
761 761  
762 762 ---
763 763  
  764 +## 其他接口
  765 +
  766 +### 搜索建议(框架)
  767 +
  768 +- **端点**: `GET /search/suggestions`
  769 +- **描述**: 返回搜索建议(自动补全/热词)。当前为框架实现,接口和响应格式已经固定,可平滑扩展。
  770 +
  771 +#### 查询参数
  772 +
  773 +| 参数 | 类型 | 必填 | 默认值 | 描述 |
  774 +|------|------|------|--------|------|
  775 +| `q` | string | ✅ | - | 查询字符串(至少 1 个字符) |
  776 +| `size` | integer | ❌ | 5 | 返回建议数量(1-20) |
  777 +| `types` | string | ❌ | `query` | 建议类型(逗号分隔):`query`, `product`, `category`, `brand` |
  778 +
  779 +#### 响应示例
  780 +
  781 +```json
  782 +{
  783 + "query": "芭",
  784 + "suggestions": [
  785 + {
  786 + "text": "芭比娃娃",
  787 + "type": "query",
  788 + "highlight": "<em>芭</em>比娃娃",
  789 + "popularity": 850
  790 + }
  791 + ],
  792 + "took_ms": 5
  793 +}
  794 +```
  795 +
  796 +#### 请求示例
  797 +
  798 +```bash
  799 +curl "http://localhost:6002/search/suggestions?q=芭&size=5&types=query,product"
  800 +```
  801 +
  802 +---
  803 +
  804 +### 即时搜索(框架)
  805 +
  806 +- **端点**: `GET /search/instant`
  807 +- **描述**: 边输入边搜索,采用轻量参数响应当前输入。底层复用标准搜索能力。
  808 +
  809 +#### 查询参数
  810 +
  811 +| 参数 | 类型 | 必填 | 默认值 | 描述 |
  812 +|------|------|------|--------|------|
  813 +| `q` | string | ✅ | - | 搜索查询(至少 2 个字符) |
  814 +| `size` | integer | ❌ | 5 | 返回结果数量(1-20) |
  815 +
  816 +#### 请求示例
  817 +
  818 +```bash
  819 +curl "http://localhost:6002/search/instant?q=玩具&size=5"
  820 +```
  821 +
  822 +---
  823 +
  824 +### 获取单个文档
  825 +
  826 +- **端点**: `GET /search/{doc_id}`
  827 +- **描述**: 根据文档 ID 获取单个商品详情,用于点击结果后的详情页或排查问题。
  828 +
  829 +#### 路径参数
  830 +
  831 +| 参数 | 类型 | 描述 |
  832 +|------|------|------|
  833 +| `doc_id` | string | 商品或文档 ID |
  834 +
  835 +#### 响应示例
  836 +
  837 +```json
  838 +{
  839 + "id": "12345",
  840 + "source": {
  841 + "title": "芭比时尚娃娃",
  842 + "min_price": 89.99,
  843 + "category_keyword": "玩具"
  844 + }
  845 +}
  846 +```
  847 +
  848 +#### 请求示例
  849 +
  850 +```bash
  851 +curl "http://localhost:6002/search/12345"
  852 +```
  853 +
  854 +---
  855 +
  856 +## 管理接口
  857 +
  858 +### 健康检查
  859 +
  860 +- **端点**: `GET /admin/health`
  861 +- **描述**: 检查服务与依赖(如 Elasticsearch)状态。
  862 +
  863 +```json
  864 +{
  865 + "status": "healthy",
  866 + "elasticsearch": "connected",
  867 + "tenant_id": "tenant1"
  868 +}
  869 +```
  870 +
  871 +---
  872 +
  873 +### 获取配置
  874 +
  875 +- **端点**: `GET /admin/config`
  876 +- **描述**: 返回当前租户的脱敏配置,便于核对索引及排序表达式。
  877 +
  878 +```json
  879 +{
  880 + "tenant_id": "tenant1",
  881 + "tenant_name": "Tenant1 Test Instance",
  882 + "es_index_name": "search_tenant1",
  883 + "num_fields": 20,
  884 + "num_indexes": 4,
  885 + "supported_languages": ["zh", "en", "ru"],
  886 + "ranking_expression": "bm25() + 0.2*text_embedding_relevance()",
  887 + "spu_enabled": false
  888 +}
  889 +```
  890 +
  891 +---
  892 +
  893 +### 索引统计
  894 +
  895 +- **端点**: `GET /admin/stats`
  896 +- **描述**: 获取索引文档数量与磁盘大小,方便监控。
  897 +
  898 +```json
  899 +{
  900 + "index_name": "search_tenant1",
  901 + "document_count": 10000,
  902 + "size_mb": 523.45
  903 +}
  904 +```
  905 +
  906 +---
  907 +
  908 +## 数据模型
  909 +
  910 +### 商品字段
  911 +
  912 +| 字段名 | 类型 | 描述 |
  913 +|--------|------|------|
  914 +| `product_id` | keyword | 商品 ID(SPU) |
  915 +| `sku_id` | keyword/long | SKU ID(主键) |
  916 +| `title` | text | 商品名称(中文) |
  917 +| `en_title` | text | 商品名称(英文) |
  918 +| `ru_title` | text | 商品名称(俄文) |
  919 +| `category_keyword` | keyword | 类目(精确匹配) |
  920 +| `vendor_keyword` | keyword | 品牌/供应商(精确匹配) |
  921 +| `product_type_keyword` | keyword | 商品类型 |
  922 +| `tags_keyword` | keyword | 标签 |
  923 +| `min_price` | double | 最低价格 |
  924 +| `max_price` | double | 最高价格 |
  925 +| `compare_at_price` | double | 原价 |
  926 +| `create_time` | date | 创建时间 |
  927 +| `update_time` | date | 更新时间 |
  928 +| `in_stock` | boolean | 是否有库存 |
  929 +| `text_embedding` | dense_vector | 文本向量(1024 维) |
  930 +| `image_embedding` | dense_vector | 图片向量(1024 维) |
  931 +
  932 +> 不同租户可自定义字段名称,但最佳实践是对可过滤字段建立 `*_keyword` 版本,对可排序字段显式建 keyword/数值映射。
  933 +
  934 +---
  935 +
  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 +
764 967 ## 附录
765 968  
766 969 ### 常用字段列表
... ... @@ -789,3 +992,27 @@ result.results.forEach(product =&gt; {
789 992 - `update_time`: 更新时间
790 993 - `relevance_score`: 相关性分数(默认)
791 994  
  995 +### 支持的分析器
  996 +
  997 +| 分析器 | 语言 | 描述 |
  998 +|--------|------|------|
  999 +| `chinese_ecommerce` | 中文 | 基于 Ansj 的电商优化中文分析器 |
  1000 +| `english` | 英文 | 标准英文分析器 |
  1001 +| `russian` | 俄文 | 俄文分析器 |
  1002 +| `arabic` | 阿拉伯文 | 阿拉伯文分析器 |
  1003 +| `spanish` | 西班牙文 | 西班牙文分析器 |
  1004 +| `japanese` | 日文 | 日文分析器 |
  1005 +
  1006 +### 字段类型速查
  1007 +
  1008 +| 类型 | ES Mapping | 用途 |
  1009 +|------|------------|------|
  1010 +| `TEXT` | `text` | 全文检索 |
  1011 +| `KEYWORD` | `keyword` | 精确匹配、聚合、排序 |
  1012 +| `LONG` | `long` | 整数 |
  1013 +| `DOUBLE` | `double` | 浮点数 |
  1014 +| `DATE` | `date` | 日期时间 |
  1015 +| `BOOLEAN` | `boolean` | 布尔值 |
  1016 +| `TEXT_EMBEDDING` | `dense_vector` | 文本语义向量 |
  1017 +| `IMAGE_EMBEDDING` | `dense_vector` | 图片语义向量 |
  1018 +
... ...
API_QUICK_REFERENCE.md renamed to docs/搜索API速查表.md
docs/TestingPipeline_README.md renamed to docs/测试Pipeline说明.md
TEST_DATA_GUIDE.md renamed to docs/测试数据指南.md
环境相关.md renamed to docs/环境配置说明.md
设计文档.md renamed to docs/系统设计文档.md
INDEX_FIELDS_DOCUMENTATION.md renamed to docs/索引字段说明.md