Blame view

api/routes/search.py 5.9 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
74
              context=context,
              aggregations=request.aggregations,
              sort_by=request.sort_by,
              sort_order=request.sort_order
be52af70   tangwang   first commit
75
76
          )
  
16c42787   tangwang   feat: implement r...
77
78
79
          # Include performance summary in response
          performance_summary = context.get_summary() if context else None
  
be52af70   tangwang   first commit
80
81
82
83
84
85
86
          # 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...
87
88
              query_info=result.query_info,
              performance_info=performance_summary
be52af70   tangwang   first commit
89
90
91
          )
  
      except Exception as e:
16c42787   tangwang   feat: implement r...
92
93
94
95
96
97
98
          # 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
99
          raise HTTPException(status_code=500, detail=str(e))
16c42787   tangwang   feat: implement r...
100
101
102
      finally:
          # Clear thread-local context
          clear_current_request_context()
be52af70   tangwang   first commit
103
104
105
  
  
  @router.post("/image", response_model=SearchResponse)
16c42787   tangwang   feat: implement r...
106
  async def search_by_image(request: ImageSearchRequest, http_request: Request):
be52af70   tangwang   first commit
107
108
109
110
111
      """
      Search by image similarity.
  
      Uses image embeddings to find visually similar products.
      """
16c42787   tangwang   feat: implement r...
112
113
114
115
116
117
118
119
      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
120
      try:
16c42787   tangwang   feat: implement r...
121
122
123
124
125
126
127
          # 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   灌入数据流程跑通
128
          from api.app import get_searcher
be52af70   tangwang   first commit
129
130
131
132
133
134
135
136
137
          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...
138
139
140
          # Include performance summary in response
          performance_summary = context.get_summary() if context else None
  
be52af70   tangwang   first commit
141
142
143
144
145
146
          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...
147
148
              query_info=result.query_info,
              performance_info=performance_summary
be52af70   tangwang   first commit
149
150
151
          )
  
      except ValueError as e:
16c42787   tangwang   feat: implement r...
152
153
154
155
156
157
          if context:
              context.set_error(e)
              context.logger.error(
                  f"图片搜索请求参数错误 | 错误: {str(e)}",
                  extra={'reqid': context.reqid, 'uid': context.uid}
              )
be52af70   tangwang   first commit
158
159
          raise HTTPException(status_code=400, detail=str(e))
      except Exception as e:
16c42787   tangwang   feat: implement r...
160
161
162
163
164
165
          if context:
              context.set_error(e)
              context.logger.error(
                  f"图片搜索请求失败 | 错误: {str(e)}",
                  extra={'reqid': context.reqid, 'uid': context.uid}
              )
be52af70   tangwang   first commit
166
          raise HTTPException(status_code=500, detail=str(e))
16c42787   tangwang   feat: implement r...
167
168
169
      finally:
          # Clear thread-local context
          clear_current_request_context()
be52af70   tangwang   first commit
170
171
172
173
174
175
176
177
  
  
  @router.get("/{doc_id}", response_model=DocumentResponse)
  async def get_document(doc_id: str):
      """
      Get a single document by ID.
      """
      try:
bb3c5ef8   tangwang   灌入数据流程跑通
178
          from api.app import get_searcher
be52af70   tangwang   first commit
179
180
181
182
183
184
185
186
187
188
189
190
191
          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))