Blame view

api/routes/search.py 5.97 KB
be52af70   tangwang   first commit
1
2
3
4
  """
  Search API routes.
  """
  
16c42787   tangwang   feat: implement r...
5
  from fastapi import APIRouter, HTTPException, Query, Request
be52af70   tangwang   first commit
6
  from typing import Optional
16c42787   tangwang   feat: implement r...
7
  import uuid
be52af70   tangwang   first commit
8
9
10
11
12
13
14
15
  
  from ..models import (
      SearchRequest,
      ImageSearchRequest,
      SearchResponse,
      DocumentResponse,
      ErrorResponse
  )
16c42787   tangwang   feat: implement r...
16
  from context.request_context import create_request_context, set_current_request_context, clear_current_request_context
be52af70   tangwang   first commit
17
18
19
20
  
  router = APIRouter(prefix="/search", tags=["search"])
  
  
16c42787   tangwang   feat: implement r...
21
22
23
24
25
26
27
28
29
30
31
  def extract_request_info(request: Request) -> tuple[str, str]:
      """Extract request ID and user ID from HTTP request"""
      # Try to get request ID from headers
      reqid = request.headers.get('X-Request-ID') or str(uuid.uuid4())[:8]
  
      # Try to get user ID from headers or default to anonymous
      uid = request.headers.get('X-User-ID') or request.headers.get('User-ID') or 'anonymous'
  
      return reqid, uid
  
  
be52af70   tangwang   first commit
32
  @router.post("/", response_model=SearchResponse)
16c42787   tangwang   feat: implement r...
33
  async def search(request: SearchRequest, http_request: Request):
be52af70   tangwang   first commit
34
35
36
37
38
39
40
41
42
43
      """
      Execute text search query.
  
      Supports:
      - Multi-language query processing
      - Boolean operators (AND, OR, RANK, ANDNOT)
      - Semantic search with embeddings
      - Custom ranking functions
      - Filters and aggregations
      """
16c42787   tangwang   feat: implement r...
44
45
46
47
48
49
50
      reqid, uid = extract_request_info(http_request)
  
      # Create request context
      context = create_request_context(reqid=reqid, uid=uid)
  
      # Set context in thread-local storage
      set_current_request_context(context)
be52af70   tangwang   first commit
51
52
  
      try:
16c42787   tangwang   feat: implement r...
53
54
55
56
57
58
59
          # Log request start
          context.logger.info(
              f"收到搜索请求 | IP: {http_request.client.host if http_request.client else 'unknown'} | "
              f"用户代理: {http_request.headers.get('User-Agent', 'unknown')[:100]}",
              extra={'reqid': context.reqid, 'uid': context.uid}
          )
  
be52af70   tangwang   first commit
60
          # Get searcher from app state
bb3c5ef8   tangwang   灌入数据流程跑通
61
          from api.app import get_searcher
be52af70   tangwang   first commit
62
63
          searcher = get_searcher()
  
16c42787   tangwang   feat: implement r...
64
          # Execute search with context (using backend defaults from config)
be52af70   tangwang   first commit
65
66
67
68
69
          result = searcher.search(
              query=request.query,
              size=request.size,
              from_=request.from_,
              filters=request.filters,
16c42787   tangwang   feat: implement r...
70
              min_score=request.min_score,
c86c8237   tangwang   支持聚合。过滤项补充了逻辑,但是有问题
71
72
73
              context=context,
              aggregations=request.aggregations,
              sort_by=request.sort_by,
1f071951   tangwang   补充调试信息,记录包括各个阶段的 ...
74
75
              sort_order=request.sort_order,
              debug=request.debug
be52af70   tangwang   first commit
76
77
          )
  
16c42787   tangwang   feat: implement r...
78
79
80
          # Include performance summary in response
          performance_summary = context.get_summary() if context else None
  
be52af70   tangwang   first commit
81
82
83
84
85
86
87
          # Convert to response model
          return SearchResponse(
              hits=result.hits,
              total=result.total,
              max_score=result.max_score,
              took_ms=result.took_ms,
              aggregations=result.aggregations,
16c42787   tangwang   feat: implement r...
88
              query_info=result.query_info,
1f071951   tangwang   补充调试信息,记录包括各个阶段的 ...
89
90
              performance_info=performance_summary,
              debug_info=result.debug_info
be52af70   tangwang   first commit
91
92
93
          )
  
      except Exception as e:
16c42787   tangwang   feat: implement r...
94
95
96
97
98
99
100
          # Log error in context
          if context:
              context.set_error(e)
              context.logger.error(
                  f"搜索请求失败 | 错误: {str(e)}",
                  extra={'reqid': context.reqid, 'uid': context.uid}
              )
be52af70   tangwang   first commit
101
          raise HTTPException(status_code=500, detail=str(e))
16c42787   tangwang   feat: implement r...
102
103
104
      finally:
          # Clear thread-local context
          clear_current_request_context()
be52af70   tangwang   first commit
105
106
107
  
  
  @router.post("/image", response_model=SearchResponse)
16c42787   tangwang   feat: implement r...
108
  async def search_by_image(request: ImageSearchRequest, http_request: Request):
be52af70   tangwang   first commit
109
110
111
112
113
      """
      Search by image similarity.
  
      Uses image embeddings to find visually similar products.
      """
16c42787   tangwang   feat: implement r...
114
115
116
117
118
119
120
121
      reqid, uid = extract_request_info(http_request)
  
      # Create request context
      context = create_request_context(reqid=reqid, uid=uid)
  
      # Set context in thread-local storage
      set_current_request_context(context)
  
be52af70   tangwang   first commit
122
      try:
16c42787   tangwang   feat: implement r...
123
124
125
126
127
128
129
          # Log request start
          context.logger.info(
              f"收到图片搜索请求 | 图片URL: {request.image_url} | "
              f"IP: {http_request.client.host if http_request.client else 'unknown'}",
              extra={'reqid': context.reqid, 'uid': context.uid}
          )
  
bb3c5ef8   tangwang   灌入数据流程跑通
130
          from api.app import get_searcher
be52af70   tangwang   first commit
131
132
133
134
135
136
137
138
139
          searcher = get_searcher()
  
          # Execute image search
          result = searcher.search_by_image(
              image_url=request.image_url,
              size=request.size,
              filters=request.filters
          )
  
16c42787   tangwang   feat: implement r...
140
141
142
          # Include performance summary in response
          performance_summary = context.get_summary() if context else None
  
be52af70   tangwang   first commit
143
144
145
146
147
148
          return SearchResponse(
              hits=result.hits,
              total=result.total,
              max_score=result.max_score,
              took_ms=result.took_ms,
              aggregations=result.aggregations,
16c42787   tangwang   feat: implement r...
149
150
              query_info=result.query_info,
              performance_info=performance_summary
be52af70   tangwang   first commit
151
152
153
          )
  
      except ValueError as e:
16c42787   tangwang   feat: implement r...
154
155
156
157
158
159
          if context:
              context.set_error(e)
              context.logger.error(
                  f"图片搜索请求参数错误 | 错误: {str(e)}",
                  extra={'reqid': context.reqid, 'uid': context.uid}
              )
be52af70   tangwang   first commit
160
161
          raise HTTPException(status_code=400, detail=str(e))
      except Exception as e:
16c42787   tangwang   feat: implement r...
162
163
164
165
166
167
          if context:
              context.set_error(e)
              context.logger.error(
                  f"图片搜索请求失败 | 错误: {str(e)}",
                  extra={'reqid': context.reqid, 'uid': context.uid}
              )
be52af70   tangwang   first commit
168
          raise HTTPException(status_code=500, detail=str(e))
16c42787   tangwang   feat: implement r...
169
170
171
      finally:
          # Clear thread-local context
          clear_current_request_context()
be52af70   tangwang   first commit
172
173
174
175
176
177
178
179
  
  
  @router.get("/{doc_id}", response_model=DocumentResponse)
  async def get_document(doc_id: str):
      """
      Get a single document by ID.
      """
      try:
bb3c5ef8   tangwang   灌入数据流程跑通
180
          from api.app import get_searcher
be52af70   tangwang   first commit
181
182
183
184
185
186
187
188
189
190
191
192
193
          searcher = get_searcher()
  
          doc = searcher.get_document(doc_id)
  
          if doc is None:
              raise HTTPException(status_code=404, detail=f"Document {doc_id} not found")
  
          return DocumentResponse(id=doc_id, source=doc)
  
      except HTTPException:
          raise
      except Exception as e:
          raise HTTPException(status_code=500, detail=str(e))