feat: paginate balanced mobile search results (Closes #783)
This commit is contained in:
1 parent
fa3e5e3bf0
commit
7b736bc4c6
8 files changed
+149
-45
No files matched your search
+19
-5
@@ -463,13 +463,14 @@ def _normalize_global_search_item(item: Any, kind: str) -> dict | None:
|
||||
}
|
||||
|
||||
|
||||
async def global_search(query: str, limit: int = 10) -> dict:
|
||||
"""Search accessible issues and pulls concurrently with a bounded result set."""
|
||||
async def global_search(query: str, limit: int = 10, page: int = 1) -> dict:
|
||||
"""Search accessible issues and pulls concurrently with balanced pagination."""
|
||||
stream_limit = (limit + 1) // 2
|
||||
async def load(item_type: str) -> Any:
|
||||
response = await _get_client().get(
|
||||
"/api/v1/repos/issues/search",
|
||||
headers=_auth(),
|
||||
params={"q": query, "type": item_type, "limit": limit, "page": 1},
|
||||
params={"q": query, "type": item_type, "limit": stream_limit, "page": page},
|
||||
)
|
||||
response.raise_for_status()
|
||||
payload = response.json()
|
||||
@@ -486,10 +487,12 @@ async def global_search(query: str, limit: int = 10) -> dict:
|
||||
if all(isinstance(outcome, BaseException) for outcome in outcomes):
|
||||
raise outcomes[0]
|
||||
|
||||
results = []
|
||||
streams: list[list[dict]] = []
|
||||
seen: set[tuple[str, str, int]] = set()
|
||||
for outcome, kind in zip(outcomes, ("issue", "pull"), strict=True):
|
||||
normalized_stream = []
|
||||
if isinstance(outcome, BaseException):
|
||||
streams.append(normalized_stream)
|
||||
continue
|
||||
for item in outcome:
|
||||
normalized = _normalize_global_search_item(item, kind)
|
||||
@@ -498,10 +501,21 @@ async def global_search(query: str, limit: int = 10) -> dict:
|
||||
if identity in seen:
|
||||
continue
|
||||
seen.add(identity)
|
||||
results.append(normalized)
|
||||
normalized_stream.append(normalized)
|
||||
streams.append(normalized_stream)
|
||||
results = []
|
||||
for index in range(max((len(stream) for stream in streams), default=0)):
|
||||
for stream in streams:
|
||||
if index < len(stream):
|
||||
results.append(stream[index])
|
||||
return {
|
||||
"items": results[:limit],
|
||||
"partial": any(isinstance(outcome, BaseException) for outcome in outcomes),
|
||||
"has_more": any(
|
||||
not isinstance(outcome, BaseException) and len(outcome) >= stream_limit
|
||||
for outcome in outcomes
|
||||
),
|
||||
"next_page": page + 1,
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -2690,13 +2690,14 @@ async def background_identity() -> JSONResponse:
|
||||
async def global_search(
|
||||
q: str = Query(min_length=2, max_length=100),
|
||||
limit: int = Query(default=10, ge=1, le=25),
|
||||
page: int = Query(default=1, ge=1, le=100),
|
||||
) -> JSONResponse:
|
||||
query = q.strip()
|
||||
if len(query) < 2:
|
||||
raise HTTPException(status_code=422, detail="Search query must contain at least 2 characters")
|
||||
try:
|
||||
result = await asyncio.wait_for(
|
||||
gitea_proxy.global_search(query, limit),
|
||||
gitea_proxy.global_search(query, limit, page),
|
||||
timeout=GLOBAL_SEARCH_TIMEOUT_SECONDS,
|
||||
)
|
||||
except Exception:
|
||||
|
||||
Reference in new issue
Block a user