diff --git a/backend/src/schema/books/resolvers/BookSearchResult.ts b/backend/src/schema/books/resolvers/BookSearchResult.ts new file mode 100644 index 0000000..1c388d6 --- /dev/null +++ b/backend/src/schema/books/resolvers/BookSearchResult.ts @@ -0,0 +1,2 @@ +import type { BookSearchResultResolvers } from "./../../types.generated"; +export const BookSearchResult: BookSearchResultResolvers = {}; diff --git a/backend/src/schema/books/resolvers/Query/book.ts b/backend/src/schema/books/resolvers/Query/book.ts new file mode 100644 index 0000000..efd2be1 --- /dev/null +++ b/backend/src/schema/books/resolvers/Query/book.ts @@ -0,0 +1,11 @@ +import type { QueryResolvers } from "./../../../types.generated"; +import { db } from "~/db"; +export const book: NonNullable = async ( + _parent, + args, + _ctx, +) => { + const foundBook = await db.query.books.findFirst({ where: { id: args.id } }); + + return foundBook ?? null; +}; diff --git a/backend/src/schema/books/resolvers/Query/searchBooks.ts b/backend/src/schema/books/resolvers/Query/searchBooks.ts index 80fe730..03ed08c 100644 --- a/backend/src/schema/books/resolvers/Query/searchBooks.ts +++ b/backend/src/schema/books/resolvers/Query/searchBooks.ts @@ -1,4 +1,6 @@ +import { and, asc, ilike, type SQL } from "drizzle-orm"; import { db } from "~/db"; +import { books } from "~/db/schema"; import type { QueryResolvers } from "~/schema/types.generated"; export const searchBooks: NonNullable = async ( @@ -6,28 +8,35 @@ export const searchBooks: NonNullable = async ( args, _ctx, ) => { - return db.query.books.findMany({ - where: { - title: { - ilike: args.title ? `%${args.title ?? ""}%` : undefined, - }, - series: { - ilike: args.series ? `%${args.series ?? ""}%` : undefined, - }, - author: { - ilike: args.author ? `%${args.author ?? ""}%` : undefined, - }, - language: { - ilike: args.language ? `%${args.language ?? ""}%` : undefined, - }, - bookFormat: { - ilike: args.bookFormat ? `%${args.bookFormat ?? ""}%` : undefined, - }, - publisher: { - ilike: args.publisher ? `%${args.publisher ?? ""}%` : undefined, - }, - }, - limit: args.limit, - offset: args.offset, - }); + const conditions: SQL[] = []; + + if (args.title) conditions.push(ilike(books.title, `%${args.title}%`)); + if (args.series) conditions.push(ilike(books.series, `%${args.series}%`)); + if (args.author) conditions.push(ilike(books.author, `%${args.author}%`)); + if (args.language) + conditions.push(ilike(books.language, `%${args.language}%`)); + if (args.bookFormat) + conditions.push(ilike(books.bookFormat, `%${args.bookFormat}%`)); + if (args.publisher) + conditions.push(ilike(books.publisher, `%${args.publisher}%`)); + + // Here we build the filter once with Drizzle's SQL builder, so the same filter + // can be used both for fetching one page and for counting all matches. + const filter = and(...conditions); + + const [foundBooks, totalCount] = await Promise.all([ + db + .select() + .from(books) + .where(filter) + .orderBy(asc(books.id)) + .limit(args.limit) + .offset(args.offset), + db.$count(books, filter), + ]); + + return { + books: foundBooks, + totalCount, + }; }; diff --git a/backend/src/schema/books/schema.graphql b/backend/src/schema/books/schema.graphql index 50cdc2f..cd16580 100644 --- a/backend/src/schema/books/schema.graphql +++ b/backend/src/schema/books/schema.graphql @@ -24,5 +24,11 @@ extend type Query { publisher: String limit: Int = 12 offset: Int = 0 - ): [Book] + ): BookSearchResult! + book(id: Int!): Book +} + +type BookSearchResult { + books: [Book!]! + totalCount: Int! } diff --git a/backend/src/schema/resolvers.generated.ts b/backend/src/schema/resolvers.generated.ts index 5f9d8f6..022f932 100644 --- a/backend/src/schema/resolvers.generated.ts +++ b/backend/src/schema/resolvers.generated.ts @@ -1,10 +1,13 @@ /* This file was automatically generated. DO NOT UPDATE MANUALLY. */ import type { Resolvers } from './types.generated'; - import { searchBooks as Query_searchBooks } from './books/resolvers/Query/searchBooks'; + import { book as Query_book } from './books/resolvers/Query/book'; +import { searchBooks as Query_searchBooks } from './books/resolvers/Query/searchBooks'; import { Book } from './books/resolvers/Book'; +import { BookSearchResult } from './books/resolvers/BookSearchResult'; export const resolvers: Resolvers = { - Query: { searchBooks: Query_searchBooks }, + Query: { book: Query_book,searchBooks: Query_searchBooks }, - Book: Book + Book: Book, +BookSearchResult: BookSearchResult } \ No newline at end of file diff --git a/backend/src/schema/schema.generated.graphqls b/backend/src/schema/schema.generated.graphqls index 3eb7209..5ac6a75 100644 --- a/backend/src/schema/schema.generated.graphqls +++ b/backend/src/schema/schema.generated.graphqls @@ -14,6 +14,12 @@ type Book { title: String! } +type BookSearchResult { + books: [Book!]! + totalCount: Int! +} + type Query { - searchBooks(author: String, bookFormat: String, language: String, limit: Int = 12, offset: Int = 0, publisher: String, series: String, title: String): [Book] + book(id: Int!): Book + searchBooks(author: String, bookFormat: String, language: String, limit: Int = 12, offset: Int = 0, publisher: String, series: String, title: String): BookSearchResult! } \ No newline at end of file diff --git a/backend/src/schema/typeDefs.generated.ts b/backend/src/schema/typeDefs.generated.ts index 33982ad..7c33094 100644 --- a/backend/src/schema/typeDefs.generated.ts +++ b/backend/src/schema/typeDefs.generated.ts @@ -1,2 +1,2 @@ import type { DocumentNode } from 'graphql'; - export const typeDefs = {"kind":"Document","definitions":[{"name":{"kind":"Name","value":"Query"},"kind":"ObjectTypeDefinition","fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"searchBooks"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"series"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"language"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"bookFormat"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"publisher"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"limit"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"12"}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"offset"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Book"}}}}],"directives":[],"interfaces":[]},{"kind":"ObjectTypeDefinition","name":{"kind":"Name","value":"Book"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"id"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"isbn"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"series"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"language"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"bookFormat"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"edition"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"pages"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"publisher"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"publishDate"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"coverImgUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}]},{"kind":"SchemaDefinition","operationTypes":[{"kind":"OperationTypeDefinition","type":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"operation":"query"}]}]} as unknown as DocumentNode \ No newline at end of file + export const typeDefs = {"kind":"Document","definitions":[{"name":{"kind":"Name","value":"Query"},"kind":"ObjectTypeDefinition","fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"searchBooks"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"series"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"language"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"bookFormat"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"publisher"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"limit"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"12"}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"offset"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"BookSearchResult"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"book"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"id"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"type":{"kind":"NamedType","name":{"kind":"Name","value":"Book"}}}],"directives":[],"interfaces":[]},{"kind":"ObjectTypeDefinition","name":{"kind":"Name","value":"Book"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"id"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"isbn"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"series"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"language"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"bookFormat"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"edition"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"pages"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"publisher"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"publishDate"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"coverImgUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}]},{"kind":"ObjectTypeDefinition","name":{"kind":"Name","value":"BookSearchResult"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"books"},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Book"}}}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"totalCount"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}]},{"kind":"SchemaDefinition","operationTypes":[{"kind":"OperationTypeDefinition","type":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"operation":"query"}]}]} as unknown as DocumentNode \ No newline at end of file diff --git a/backend/src/schema/types.generated.ts b/backend/src/schema/types.generated.ts index e7dc620..a4712cf 100644 --- a/backend/src/schema/types.generated.ts +++ b/backend/src/schema/types.generated.ts @@ -28,9 +28,21 @@ export type Book = { title: Scalars['String']['output']; }; +export type BookSearchResult = { + __typename?: 'BookSearchResult'; + books: Array; + totalCount: Scalars['Int']['output']; +}; + export type Query = { __typename?: 'Query'; - searchBooks?: Maybe>>; + book?: Maybe; + searchBooks: BookSearchResult; +}; + + +export type QuerybookArgs = { + id: Scalars['Int']['input']; }; @@ -121,6 +133,7 @@ export type ResolversTypes = { Book: ResolverTypeWrapper; String: ResolverTypeWrapper; Int: ResolverTypeWrapper; + BookSearchResult: ResolverTypeWrapper; Query: ResolverTypeWrapper>; Boolean: ResolverTypeWrapper; }; @@ -130,6 +143,7 @@ export type ResolversParentTypes = { Book: Book; String: Scalars['String']['output']; Int: Scalars['Int']['output']; + BookSearchResult: BookSearchResult; Query: Record; Boolean: Scalars['Boolean']['output']; }; @@ -150,12 +164,19 @@ export type BookResolvers; }; +export type BookSearchResultResolvers = { + books?: Resolver, ParentType, ContextType>; + totalCount?: Resolver; +}; + export type QueryResolvers = { - searchBooks?: Resolver>>, ParentType, ContextType, RequireFields>; + book?: Resolver, ParentType, ContextType, RequireFields>; + searchBooks?: Resolver>; }; export type Resolvers = { Book?: BookResolvers; + BookSearchResult?: BookSearchResultResolvers; Query?: QueryResolvers; };