feat: render search on server
This commit is contained in:
79
lib/search.ts
Normal file
79
lib/search.ts
Normal file
@ -0,0 +1,79 @@
|
||||
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,
|
||||
});
|
||||
}
|
@ -54,4 +54,9 @@ export type TypesenseDocument = {
|
||||
image?: string;
|
||||
};
|
||||
|
||||
export enum ResourceStatus {
|
||||
COMPLETED = "completed",
|
||||
NOT_COMPLETED = "not_completed",
|
||||
}
|
||||
|
||||
export type SearchResult = SearchResponse<TypesenseDocument>;
|
||||
|
Reference in New Issue
Block a user