memorium/lib/search.ts

80 lines
1.9 KiB
TypeScript

import { BadRequestError } from "@lib/errors.ts";
import { resources } from "@lib/resources.ts";
import { ResourceStatus } 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[];
status?: ResourceStatus;
query_by?: string;
};
export function parseResourceUrl(_url: string): SearchParams | undefined {
try {
const url = new 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,
status: url.searchParams.get("status") as ResourceStatus || 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, status }:
SearchParams,
) {
const typesenseClient = await getTypeSenseClient();
if (!typesenseClient) {
throw new Error("Query not available");
}
const filter_by: string[] = [];
if (type) {
filter_by.push(`type:=${type}`);
}
if (status) {
filter_by.push(`status:=${status}`);
}
if (tags?.length) {
filter_by.push(`tags:[${tags.map((t) => `\`${t}\``).join(",")}]`);
for (const tag of tags) {
q = q.replaceAll(`#${tag}`, "");
}
}
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,
});
}