feat(search+docs): trigram fuzzy search, brand/country filters, developer docs
CI / Python (ingestion) (pull_request) Successful in 12s
CI / Migrations (postgres) (pull_request) Successful in 22s
CI / Go (api) (pull_request) Successful in 47s

Search:
- migration 0009: trigram GIN index on brand.name + btree on country_of_origin
- SearchProducts: typo-tolerant word_similarity matching (>=0.42) on top of
  ILIKE substring + barcode; new brand/country filters; rank by
  similarity * (0.5 + quality_score). Response gains country_of_origin,
  quality_score and per-result relevance score.
- public search UI: brand/country filter inputs; show country in results

Docs:
- serve embedded OpenAPI 3 spec at GET /api/v1/openapi.json (not rate limited)
- ApiDocs page: auth + rate-limit section, updated search params/response
- docs/api.md developer guide

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
novaalphastrikeomegaz663
2026-06-20 09:38:27 +00:00
parent cf255e2380
commit 69a0149bbe
11 changed files with 613 additions and 48 deletions
+15 -4
View File
@@ -25,11 +25,22 @@ export interface SearchResult {
total: number;
}
export interface SearchFilters {
brand?: string;
country?: string;
}
export const api = {
search: (q: string, page = 1, size = 20) =>
req<SearchResult>(
`/api/v1/products/search?q=${encodeURIComponent(q)}&page=${page}&size=${size}`,
),
search: (q: string, page = 1, size = 20, filters: SearchFilters = {}) => {
const params = new URLSearchParams({
q,
page: String(page),
size: String(size),
});
if (filters.brand) params.set("brand", filters.brand);
if (filters.country) params.set("country", filters.country);
return req<SearchResult>(`/api/v1/products/search?${params.toString()}`);
},
product: (id: string) => req<Product>(`/api/v1/products/${id}`),
categories: () => req<{ items: Category[] }>(`/api/v1/categories`),
submit: (input: SubmissionInput) =>