import { resources } from "@lib/resources.ts"; import { SearchResult } from "@lib/types.ts"; import { getTypeSenseClient } from "@lib/typesense.ts"; import { extractHashTags } from "@lib/string.ts"; type ResourceType = keyof typeof resources; type SearchParams = { q: string; type?: ResourceType; tags?: string[]; rating?: string; author?: string; query_by?: string; }; export function parseResourceUrl(_url: string | URL): SearchParams | undefined { try { const url = typeof _url === "string" ? new URL(_url) : _url; let query = url.searchParams.get("q") || "*"; if (!query) { return undefined; } query = decodeURIComponent(query); const hashTags = extractHashTags(query); for (const tag of hashTags) { query = query.replace("#" + tag, ""); } return { q: query, type: url.searchParams.get("type") as ResourceType || undefined, tags: hashTags, rating: url.searchParams.get("rating") || undefined, query_by: url.searchParams.get("query_by") || undefined, }; } catch (_err) { return undefined; } } export async function searchResource( { q, query_by = "name,description,author,tags", tags = [], type, rating }: SearchParams, ): Promise { const typesenseClient = await getTypeSenseClient(); if (!typesenseClient) { throw new Error("Query not available"); } const filter_by: string[] = []; if (type) { filter_by.push(`type:=${type}`); } if (tags?.length) { filter_by.push(`tags:[${tags.map((t) => `\`${t}\``).join(",")}]`); for (const tag of tags) { q = q.replaceAll(`#${tag}`, ""); } if (!q.trim().length) { q = "*"; } } if (typeof rating !== "undefined") { if (rating === "null") { filter_by.push(`rating: null`); } else { filter_by.push(`rating: ${rating}`); } } return await typesenseClient.collections("resources") .documents().search({ q, query_by, facet_by: "rating,author,tags", max_facet_values: 10, filter_by: filter_by.join(" && "), per_page: 50, }); }