fix: use stable search catalog codes

This commit is contained in:
2026-08-25 08:26:48 +03:00
parent 0ef6ad4d20
commit 2ef33c5cc3
6 changed files with 78 additions and 37 deletions

View File

@@ -3,7 +3,6 @@
from __future__ import annotations
import argparse
import base64
import datetime
import json
import re
@@ -12,6 +11,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from search.minjust_opensearch import APP_VERSION, request_json
from search.catalog import CATALOGS, labels
API_VERSION = "v1"
@@ -56,26 +56,6 @@ def date(value: str | None, name: str) -> str | None:
return value
def catalog_code(name: str, labels: tuple[str | None, str | None]) -> str:
value = json.dumps(labels, ensure_ascii=False, separators=(",", ":")).encode()
return f"{name}:v1:{base64.urlsafe_b64encode(value).decode().rstrip('=')}"
def catalog_label(name: str, code: str, language: str) -> str:
prefix = f"{name}:v1:"
if not code.startswith(prefix):
raise ApiError(400, f"{name} must be a catalog code")
try:
encoded = code[len(prefix):]
values = json.loads(base64.urlsafe_b64decode(encoded + "=" * (-len(encoded) % 4)))
value = values[0 if language == "ru" else 1]
except (IndexError, TypeError, ValueError, UnicodeError, json.JSONDecodeError) as error:
raise ApiError(400, f"{name} must be a catalog code") from error
if not isinstance(values, list) or len(values) != 2 or not isinstance(value, str):
raise ApiError(400, f"{name} must be a catalog code")
return value
def openapi() -> dict:
responses = {"200": {"description": "Successful response"}, "400": {"description": "Invalid request"}, "404": {"description": "Not found"}, "502": {"description": "Search backend unavailable"}}
return {
@@ -134,11 +114,13 @@ class Api:
if sort not in {"relevance", "date"}:
raise ApiError(400, "sort must be relevance or date")
filters: list[dict] = [{"term": {"language": language}}, {"term": {"is_current_edition": True}}]
fields = {"document_type": f"document_type_{language}", "status": f"status_{language}", "authority": f"authority_paths_{language}"}
fields = {"document_type": "document_type_code", "status": "status_code", "authority": "authority_codes"}
for parameter, field in fields.items():
value = one(query, parameter)
if value:
filters.append({"term": {field: catalog_label(parameter, value, language)}})
if value not in CATALOGS[parameter]:
raise ApiError(400, f"{parameter} must be a catalog code")
filters.append({"term": {field: value}})
date_from, date_to = date(one(query, "date_from"), "date_from"), date(one(query, "date_to"), "date_to")
if date_from and date_to and date_from > date_to:
raise ApiError(400, "date_from must not be later than date_to")
@@ -174,13 +156,13 @@ class Api:
language = one(query, "language") or "ru"
if language not in LANGUAGES:
raise ApiError(400, "language must be ru or ky")
fields = {"document_types": ("document_type", "document_type_ru", "document_type_ky"), "statuses": ("status", "status_ru", "status_ky"), "authorities": ("authority", "authority_paths_ru", "authority_paths_ky")}
body = {"size": 0, "query": {"term": {"is_current_edition": True}}, "aggs": {name: {"multi_terms": {"terms": [{"field": field} for field in pair[1:]], "size": 1000}, "aggs": {"documents": {"cardinality": {"field": "document_code", "precision_threshold": 40000}}}} for name, pair in fields.items()}}
fields = {"document_types": ("document_type", "document_type_code"), "statuses": ("status", "status_code"), "authorities": ("authority", "authority_codes")}
body = {"size": 0, "query": {"term": {"is_current_edition": True}}, "aggs": {name: {"terms": {"field": pair[1], "size": 1000}, "aggs": {"documents": {"cardinality": {"field": "document_code", "precision_threshold": 40000}}}} for name, pair in fields.items()}}
response = self.query_opensearch(body)
try:
aggregations = response["aggregations"]
values = {
name: [{"code": catalog_code(pair[0], (item["key"][0], item["key"][1])), "labels": {"ru": item["key"][0], "ky": item["key"][1]}, "count": item["documents"]["value"]} for item in aggregations[name]["buckets"]]
name: [{"code": item["key"], "labels": labels(pair[0], item["key"]), "count": item["documents"]["value"]} for item in aggregations[name]["buckets"]]
for name, pair in fields.items()
}
except (KeyError, TypeError) as error: