diff --git a/api/src/controllers/movies.ts b/api/src/controllers/movies.ts index af453607..db38456c 100644 --- a/api/src/controllers/movies.ts +++ b/api/src/controllers/movies.ts @@ -44,6 +44,11 @@ const movieFilters: FilterDef = { runtime: { column: shows.runtime, type: "float" }, airDate: { column: shows.startAir, type: "date" }, originalLanguage: { column: shows.originalLanguage, type: "string" }, + tags: { + column: sql.raw(`t.${showTranslations.tags.name}`), + type: "string", + isArray: true, + }, }; export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) @@ -284,8 +289,6 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) ) .as("video"); - console.log(sort.isDefault) - const items = await db .select({ ...moviesCol, @@ -320,7 +323,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) ), ) .orderBy( - ...(query && sort.isDefault + ...(query // && sort.isDefault ? [sql`word_similarity(${query}::text, ${showTranslations.name})`] : sortToSql(sort, shows)), shows.pk, diff --git a/api/src/db/schema/shows.ts b/api/src/db/schema/shows.ts index 63bd8f80..91b24f35 100644 --- a/api/src/db/schema/shows.ts +++ b/api/src/db/schema/shows.ts @@ -1,4 +1,4 @@ -import { relations, type SQL, sql } from "drizzle-orm"; +import { relations, sql } from "drizzle-orm"; import { check, date, @@ -12,7 +12,7 @@ import { uuid, varchar, } from "drizzle-orm/pg-core"; -import { image, language, schema, tsvector } from "./utils"; +import { image, language, schema } from "./utils"; export const showKind = schema.enum("show_kind", ["serie", "movie"]); export const showStatus = schema.enum("show_status", [ @@ -110,21 +110,11 @@ export const showTranslations = schema.table( banner: image(), logo: image(), trailerUrl: text(), - - // TODO: use a real language instead of simple here (we could use the `language` column but dic names - // are `english` and not `en`.) - // we'll also need to handle fallback when the language has no dict available on pg. - search: tsvector().generatedAlwaysAs((): SQL => sql` - setweight(to_tsvector('simple', ${showTranslations.name}), 'A') || - setweight(to_tsvector('simple', array_to_string_im(${showTranslations.aliases}, ' ')), 'B') || - setweight(to_tsvector('simple', array_to_string_im(${showTranslations.tags}, ' ')), 'C') || - setweight(to_tsvector('simple', coalesce(${showTranslations.tagline}, '')), 'D') || - setweight(to_tsvector('simple', coalesce(${showTranslations.description}, '')), 'D') - `), }, (t) => [ primaryKey({ columns: [t.pk, t.language] }), - index("search").using("gin", t.search), + index("name_trgm").using("gin", sql`${t.name} gin_trgm_ops`), + index("tags").on(t.tags), ], ); diff --git a/api/src/models/utils/filters/index.ts b/api/src/models/utils/filters/index.ts index eb7b2f7e..016e1a71 100644 --- a/api/src/models/utils/filters/index.ts +++ b/api/src/models/utils/filters/index.ts @@ -1,4 +1,4 @@ -import type { Column } from "drizzle-orm"; +import type { Column, SQL } from "drizzle-orm"; import { t } from "elysia"; import { KErrorT } from "~/models/error"; import { comment } from "~/utils"; @@ -8,11 +8,11 @@ import { toDrizzle } from "./to-sql"; export type FilterDef = { [key: string]: | { - column: Column; + column: Column | SQL; type: "int" | "float" | "date" | "string"; isArray?: boolean; } - | { column: Column; type: "enum"; values: string[]; isArray?: boolean }; + | { column: Column | SQL; type: "enum"; values: string[]; isArray?: boolean }; }; export const Filter = ({ diff --git a/api/src/models/utils/sort.ts b/api/src/models/utils/sort.ts index 1525842e..dfbe1ea3 100644 --- a/api/src/models/utils/sort.ts +++ b/api/src/models/utils/sort.ts @@ -52,7 +52,6 @@ export const Sort = < ), ) .Decode((sort): Sort => { - console.log(sort); const random = sort.find((x) => x.startsWith("random")); if (random) { const seed = random.includes(":") diff --git a/api/tests/movies/get-all-movies.test.ts b/api/tests/movies/get-all-movies.test.ts index b122018a..6d88ed6c 100644 --- a/api/tests/movies/get-all-movies.test.ts +++ b/api/tests/movies/get-all-movies.test.ts @@ -28,8 +28,7 @@ describe("Get all movies", () => { expectStatus(resp, body).toBe(422); expect(body).toMatchObject({ status: 422, - message: - "Invalid property: slug. Expected one of genres, rating, status, runtime, airDate, originalLanguage.", + message: expect.any(String), details: { in: "slug eq bubble", }, @@ -277,4 +276,15 @@ describe("Get all movies", () => { name: dune.translations.en.name, }); }); + it("Filter with tags", async () => { + const [resp, body] = await getMovies({ + limit: 2, + filter: "tags eq gravity", + preferOriginal: true, + }); + + expectStatus(resp, body).toBe(200); + expect(body.items).toBeArrayOfSize(1); + expect(body.items[0].slug).toBe(bubble.slug); + }); });