From ef2d526aa9c238125594093863e4b17d8510907f Mon Sep 17 00:00:00 2001 From: albdert Date: Mon, 12 Oct 2026 14:02:29 +0200 Subject: [PATCH] search --- backend/src/resolvers.ts | 67 +++++++++++++++++++++++++--------------- backend/src/schema.ts | 2 +- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/backend/src/resolvers.ts b/backend/src/resolvers.ts index bbbca36..3df9190 100644 --- a/backend/src/resolvers.ts +++ b/backend/src/resolvers.ts @@ -1,7 +1,15 @@ import { pool } from './db.js'; +// type definitions +type GamesArgs = { + search?: string | null; + first: number; +}; + +// const vars and func const VALID_ID_REGEX = /^\d{1,18}$/; const MAX_PAGE_SIZE = 50; +const escapeChars = (s: string) => s.replace(/[\\%_]/g, '\\$&') // definerer hvilke kolonner som skal hentes for en generell // forespørsel om et spill, inkludert oversetting fra databasen @@ -22,23 +30,51 @@ const GAME_COLS = ` `; // SQL queries +const GAMES_PATTERN = ` + WHERE ($1::text IS NULL + OR search_names ILIKE $1) +`; const SQL_COUNT = ` SELECT count(*)::int AS count FROM games + ${GAMES_PATTERN} `; - const SQL_GAME = ` SELECT ${GAME_COLS} FROM games WHERE id = $1 `; - const SQL_GAMES = ` SELECT ${GAME_COLS} FROM games + ${GAMES_PATTERN} ORDER BY added DESC, id DESC - LIMIT $1 + LIMIT $2 `; +// game resolvers +async function game(_parent: unknown, args: { id: string }) { + if (!VALID_ID_REGEX.test(args.id)) { return null; } + const res = await pool.query(SQL_GAME, [args.id]) + return res.rows[0] ?? null; +}; + +async function games(_parent: unknown, args: GamesArgs ) { + const term = args.search?.trim(); + const pattern = term?`%${escapeChars(term)}%`:null; + const first = Math.min(Math.max(args.first, 1), MAX_PAGE_SIZE); + + const res = await pool.query(SQL_GAMES, [pattern, first]); + return { + edges: res.rows.map((row) => ({ node: row, cursor: '' })), + pageInfo: { hasNextPage: false, endCursor: null}, + pattern, + }; +} + +async function totalCount(parent: { pattern: string | null }) { + return (await pool.query(SQL_COUNT, [parent.pattern])).rows[0].count; +} + // resolver for koblings-tabellene i databasen -const relation = (table: string, junc: string, fk: string) => { +function relation(table: string, junc: string, fk: string) { const sql = ` SELECT t.id, t.name FROM ${junc} j @@ -54,24 +90,7 @@ const relation = (table: string, junc: string, fk: string) => { // resolvers for å hente kjøre spørringer mot databasen export const resolvers = { - Query: { - // ett spill basert på id - game: async (_parent: unknown, args: { id: string }) => { - if (!VALID_ID_REGEX.test(args.id)) { return null; } - const res = await pool.query(SQL_GAME, [args.id]) - return res.rows[0] ?? null; - }, - - // liste med spill formattert som en GameConnection - games: async (_parent: unknown, args: { first: number }) => { - const first = Math.min(Math.max(args.first, 1), MAX_PAGE_SIZE); - const res = await pool.query(SQL_GAMES, [first]); - return { - edges: res.rows.map((row) => ({ node: row, cursor: '' })), - pageInfo: { hasNextPage: false, endCursor: null}, - }; - } - }, + Query: { game, games }, Game: { genres: relation('genres', 'game_genres', 'genre_id'), @@ -83,7 +102,5 @@ export const resolvers = { tags: relation('tags', 'game_tags', 'tag_id'), }, - GameConnection: { - totalCount: async() => { return (await pool.query(SQL_COUNT)).rows[0].count; } - } + GameConnection: { totalCount } }; diff --git a/backend/src/schema.ts b/backend/src/schema.ts index c18d3a3..e8bb086 100644 --- a/backend/src/schema.ts +++ b/backend/src/schema.ts @@ -66,6 +66,6 @@ export const typeDefs = `#graphql """ type Query { game(id: ID!): Game - games(first: Int=20): GameConnection! + games(search: String, first: Int=20): GameConnection! } `; \ No newline at end of file