|
3 | 3 | import json |
4 | 4 | from typing import Annotated, Any, Literal |
5 | 5 |
|
| 6 | +import web |
6 | 7 | from fastapi import APIRouter, Depends, Query, Request |
7 | 8 | from pydantic import ( |
8 | 9 | BaseModel, |
|
16 | 17 | from openlibrary.core.fulltext import fulltext_search_async |
17 | 18 | from openlibrary.fastapi.models import Pagination, PaginationLimit20 |
18 | 19 | from openlibrary.plugins.worksearch.code import ( |
| 20 | + async_run_solr_query, |
19 | 21 | default_spellcheck_count, |
20 | 22 | validate_search_json_query, |
21 | 23 | work_search_async, |
22 | 24 | ) |
| 25 | +from openlibrary.plugins.worksearch.schemes.authors import AuthorSearchScheme |
| 26 | +from openlibrary.plugins.worksearch.schemes.lists import ListSearchScheme |
| 27 | +from openlibrary.plugins.worksearch.schemes.subjects import SubjectSearchScheme |
23 | 28 | from openlibrary.plugins.worksearch.schemes.works import WorkSearchScheme |
24 | 29 |
|
25 | 30 | router = APIRouter() |
@@ -206,3 +211,89 @@ async def search_inside_json( |
206 | 211 | return await fulltext_search_async( |
207 | 212 | q, page=pagination.page, limit=pagination.limit, js=True, facets=True |
208 | 213 | ) |
| 214 | + |
| 215 | + |
| 216 | +@router.get("/search/subjects.json") |
| 217 | +async def search_subjects_json( |
| 218 | + pagination: Annotated[Pagination, Depends()], |
| 219 | + q: str = Query("", description="The search query"), |
| 220 | +): |
| 221 | + response = await async_run_solr_query( |
| 222 | + SubjectSearchScheme(), |
| 223 | + {'q': q}, |
| 224 | + offset=pagination.offset, |
| 225 | + rows=pagination.limit, |
| 226 | + sort='work_count desc', |
| 227 | + request_label='SUBJECT_SEARCH_API', |
| 228 | + ) |
| 229 | + |
| 230 | + # Backward compatibility |
| 231 | + raw_resp = response.raw_resp['response'] |
| 232 | + for doc in raw_resp['docs']: |
| 233 | + doc['type'] = doc.get('subject_type', 'subject') |
| 234 | + doc['count'] = doc.get('work_count', 0) |
| 235 | + |
| 236 | + return raw_resp |
| 237 | + |
| 238 | + |
| 239 | +@router.get("/search/lists.json") |
| 240 | +async def search_lists_json( |
| 241 | + pagination: Annotated[PaginationLimit20, Depends()], |
| 242 | + q: str = Query("", description="The search query"), |
| 243 | + fields: str = Query("", description="Fields to return"), |
| 244 | + sort: str = Query("", description="Sort order"), |
| 245 | + api: str = Query( |
| 246 | + "", description="API version: 'next' for new format, empty for old format" |
| 247 | + ), |
| 248 | +): |
| 249 | + response = await async_run_solr_query( |
| 250 | + ListSearchScheme(), |
| 251 | + {'q': q}, |
| 252 | + offset=pagination.offset, |
| 253 | + rows=pagination.limit, |
| 254 | + fields=fields, |
| 255 | + sort=sort, |
| 256 | + request_label='LIST_SEARCH_API', |
| 257 | + ) |
| 258 | + |
| 259 | + if api == 'next': |
| 260 | + # Match search.json |
| 261 | + return { |
| 262 | + 'numFound': response.num_found, |
| 263 | + 'num_found': response.num_found, |
| 264 | + 'start': pagination.offset, |
| 265 | + 'q': q, |
| 266 | + 'docs': response.docs, |
| 267 | + } |
| 268 | + else: |
| 269 | + # Default to the old API shape for a while, then we'll flip |
| 270 | + lists = web.ctx.site.get_many([doc['key'] for doc in response.docs]) |
| 271 | + return { |
| 272 | + 'start': pagination.offset, |
| 273 | + 'docs': [lst.preview() for lst in lists], |
| 274 | + } |
| 275 | + |
| 276 | + |
| 277 | +@router.get("/search/authors.json") |
| 278 | +async def search_authors_json( |
| 279 | + pagination: Annotated[Pagination, Depends()], |
| 280 | + q: str = Query("", description="The search query"), |
| 281 | + fields: str = Query("*", description="Fields to return"), |
| 282 | + sort: str = Query("", description="Sort order"), |
| 283 | +): |
| 284 | + response = await async_run_solr_query( |
| 285 | + AuthorSearchScheme(), |
| 286 | + {'q': q}, |
| 287 | + offset=pagination.offset, |
| 288 | + rows=pagination.limit, |
| 289 | + fields=fields, |
| 290 | + sort=sort, |
| 291 | + request_label='AUTHOR_SEARCH_API', |
| 292 | + ) |
| 293 | + |
| 294 | + # SIGH the public API exposes the key like this :( |
| 295 | + raw_resp = response.raw_resp['response'] |
| 296 | + for doc in raw_resp['docs']: |
| 297 | + doc['key'] = doc['key'].split('/')[-1] |
| 298 | + |
| 299 | + return raw_resp |
0 commit comments