`\`${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,
+ });
+}
diff --git a/lib/types.ts b/lib/types.ts
index 5157a90..c503388 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -54,4 +54,9 @@ export type TypesenseDocument = {
image?: string;
};
+export enum ResourceStatus {
+ COMPLETED = "completed",
+ NOT_COMPLETED = "not_completed",
+}
+
export type SearchResult = SearchResponse
;
diff --git a/routes/api/resources.ts b/routes/api/resources.ts
index 20a815c..4d3cf1c 100644
--- a/routes/api/resources.ts
+++ b/routes/api/resources.ts
@@ -2,7 +2,7 @@ import { Handlers } from "$fresh/server.ts";
import { AccessDeniedError, BadRequestError } from "@lib/errors.ts";
import { getTypeSenseClient } from "@lib/typesense.ts";
import { json } from "@lib/helpers.ts";
-import { extractHashTags } from "@lib/string.ts";
+import { parseResourceUrl, searchResource } from "@lib/search.ts";
export const handler: Handlers = {
async GET(req, ctx) {
@@ -11,52 +11,10 @@ export const handler: Handlers = {
throw new AccessDeniedError();
}
- const url = new URL(req.url);
- let query = url.searchParams.get("q");
- if (!query) {
- throw new BadRequestError('Query parameter "q" is required.');
- }
- query = decodeURIComponent(query);
-
- const query_by = url.searchParams.get("query_by") ||
- "name,description,author,tags";
-
- const filter_by: string[] = [];
- const type = url.searchParams.get("type");
- if (type) {
- filter_by.push(`type:=${type}`);
- }
-
- const status = url.searchParams.get("status");
- if (status) {
- filter_by.push(`status:=${status}`);
- }
-
- const hashTags = extractHashTags(query);
- if (hashTags?.length) {
- filter_by.push(`tags:[${hashTags.map((t) => `\`${t}\``).join(",")}]`);
- for (const tag of hashTags) {
- query = query.replaceAll(`#${tag}`, "");
- }
- }
-
- const typesenseClient = await getTypeSenseClient();
- if (!typesenseClient) {
- throw new Error("Query not available");
- }
-
- console.log({ query, query_by, filter_by: filter_by.join(" && ") });
+ const searchParams = parseResourceUrl(req.url);
// Perform the Typesense search
- const searchResults = await typesenseClient.collections("resources")
- .documents().search({
- q: query,
- query_by,
- facet_by: "rating,author,tags",
- max_facet_values: 10,
- filter_by: filter_by.join(" && "),
- per_page: 50,
- });
+ const searchResults = await searchResource(searchParams);
return json(searchResults);
},
diff --git a/routes/articles/index.tsx b/routes/articles/index.tsx
index 1cef702..b69361f 100644
--- a/routes/articles/index.tsx
+++ b/routes/articles/index.tsx
@@ -6,17 +6,30 @@ import { KMenu } from "@islands/KMenu.tsx";
import { Grid } from "@components/Grid.tsx";
import { IconArrowLeft } from "@components/icons.tsx";
import { RedirectSearchHandler } from "@islands/Search.tsx";
+import { parseResourceUrl, searchResource } from "@lib/search.ts";
+import { SearchResult } from "@lib/types.ts";
export const handler: Handlers = {
- async GET(_, ctx) {
- const movies = await getAllArticles();
- return ctx.render(movies);
+ async GET(req, ctx) {
+ const articles = await getAllArticles();
+ const searchParams = parseResourceUrl(req.url);
+ const searchResults = searchParams &&
+ await searchResource({ ...searchParams, type: "article" });
+ return ctx.render({ articles, searchResults });
},
};
-export default function Greet(props: PageProps) {
+export default function Greet(
+ props: PageProps<{ articles: Article[] | null; searchResults: SearchResult }>,
+) {
+ const { articles, searchResults } = props.data;
return (
-
+
) {
- {props.data?.map((doc) => {
+ {articles?.map((doc) => {
return (
= {
- async GET(_, ctx) {
+ async GET(req, ctx) {
const movies = await getAllMovies();
- return ctx.render(movies);
+ const searchParams = parseResourceUrl(req.url);
+ const searchResults = searchParams &&
+ await searchResource({ ...searchParams, type: "movie" });
+ return ctx.render({ movies, searchResults });
},
};
-export default function Greet(props: PageProps) {
+export default function Greet(
+ props: PageProps<{ movies: Movie[] | null; searchResults: SearchResult }>,
+) {
+ const { movies, searchResults } = props.data;
+
return (
-
+
@@ -31,7 +45,7 @@ export default function Greet(props: PageProps) {
🍿 Movies
- {props.data?.map((doc) => {
+ {movies?.map((doc) => {
return ;
})}
diff --git a/routes/recipes/index.tsx b/routes/recipes/index.tsx
index 29dbc71..babe372 100644
--- a/routes/recipes/index.tsx
+++ b/routes/recipes/index.tsx
@@ -5,18 +5,31 @@ import { getAllRecipes, Recipe } from "@lib/resource/recipes.ts";
import { Grid } from "@components/Grid.tsx";
import { IconArrowLeft } from "@components/icons.tsx";
import { KMenu } from "@islands/KMenu.tsx";
-import { RedirectSearchHandler } from "@islands/Search.tsx";
+import { fetchQueryResource, RedirectSearchHandler } from "@islands/Search.tsx";
+import { parseResourceUrl, searchResource } from "@lib/search.ts";
+import { SearchResult } from "@lib/types.ts";
export const handler: Handlers = {
- async GET(_, ctx) {
+ async GET(req, ctx) {
const recipes = await getAllRecipes();
- return ctx.render(recipes);
+ const searchParams = parseResourceUrl(req.url);
+ const searchResults = searchParams &&
+ await searchResource({ ...searchParams, type: "recipe" });
+ return ctx.render({ recipes, searchResults });
},
};
-export default function Greet(props: PageProps) {
+export default function Greet(
+ props: PageProps<{ recipes: Recipe[] | null; searchResults: SearchResult }>,
+) {
+ const { recipes, searchResults } = props.data;
return (
-
+
@@ -31,7 +44,7 @@ export default function Greet(props: PageProps) {
🍽️ Recipes
- {props.data?.map((doc) => {
+ {recipes?.map((doc) => {
return ;
})}
diff --git a/routes/series/index.tsx b/routes/series/index.tsx
index 0789727..ef812b9 100644
--- a/routes/series/index.tsx
+++ b/routes/series/index.tsx
@@ -6,17 +6,31 @@ import { getAllSeries, Series } from "@lib/resource/series.ts";
import { RedirectSearchHandler } from "@islands/Search.tsx";
import { KMenu } from "@islands/KMenu.tsx";
import { MovieCard } from "@components/MovieCard.tsx";
+import { parseResourceUrl, searchResource } from "@lib/search.ts";
+import { SearchResult } from "@lib/types.ts";
export const handler: Handlers = {
- async GET(_, ctx) {
- const movies = await getAllSeries();
- return ctx.render(movies);
+ async GET(req, ctx) {
+ const series = await getAllSeries();
+ const searchParams = parseResourceUrl(req.url);
+ const searchResults = searchParams &&
+ await searchResource({ ...searchParams, type: "series" });
+ return ctx.render({ series, searchResults });
},
};
-export default function Greet(props: PageProps) {
+export default function Greet(
+ props: PageProps<{ series: Series[] | null; searchResults: SearchResult }>,
+) {
+ const { series, searchResults } = props.data;
+
return (
-
+
@@ -31,7 +45,7 @@ export default function Greet(props: PageProps) {
🎥 Series
- {props.data?.map((doc) => {
+ {series?.map((doc) => {
return ;
})}