Commit 4a677843805747d44b86f3e8394a9244a2ea4c19

Authored by tangwang
1 parent 670c701f

文档完善

API_DOCUMENTATION.md deleted
@@ -1,938 +0,0 @@ @@ -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,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,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 -  
@@ -15,12 +15,12 @@ @@ -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 > README 仅保留最常用命令的“索引”。细节以主题文档为准。 25 > README 仅保留最常用命令的“索引”。细节以主题文档为准。
26 26
@@ -52,19 +52,21 @@ curl -X POST http://localhost:6002/search/ \ @@ -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 - `商品数据源入ES配置规范.md`:数据源映射约定 70 - `商品数据源入ES配置规范.md`:数据源映射约定
69 - `MULTILANG_FEATURE.md`:多语言处理细节 71 - `MULTILANG_FEATURE.md`:多语言处理细节
70 72
@@ -73,12 +75,12 @@ curl -X POST http://localhost:6002/search/ \ @@ -73,12 +75,12 @@ curl -X POST http://localhost:6002/search/ \
73 - **数据构建 → MySQL → Elasticsearch** 75 - **数据构建 → MySQL → Elasticsearch**
74 - `scripts/mock_data.sh`:Tenant1 Mock + Tenant2 CSV 一条龙 76 - `scripts/mock_data.sh`:Tenant1 Mock + Tenant2 CSV 一条龙
75 - `scripts/ingest.sh <tenant_id> [recreate]`:驱动 `indexer/` 模块写入 `search_products` 77 - `scripts/ingest.sh <tenant_id> [recreate]`:驱动 `indexer/` 模块写入 `search_products`
76 - - 详解:`TEST_DATA_GUIDE.md` 78 + - 详解:`测试数据指南.md`
77 79
78 - **搜索服务 & API** 80 - **搜索服务 & API**
79 - `api/`(FastAPI)承载 REST API,`search/` + `query/` 负责查询解析与下发 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 - `config/schema/{tenant_id}/config.yaml`:字段定义、索引域、排序表达式、SPU 聚合 86 - `config/schema/{tenant_id}/config.yaml`:字段定义、索引域、排序表达式、SPU 聚合
@@ -96,16 +98,3 @@ scripts/ 数据/服务脚本(mock_data, ingest, run 等) @@ -96,16 +98,3 @@ scripts/ 数据/服务脚本(mock_data, ingest, run 等)
96 frontend/ 简易调试页面 98 frontend/ 简易调试页面
97 docs/ 运营及中文资料 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,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 \ No newline at end of file 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,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,3 +992,27 @@ result.results.forEach(product =&gt; {
789 - `update_time`: 更新时间 992 - `update_time`: 更新时间
790 - `relevance_score`: 相关性分数(默认) 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