From b3523b4c631c4f507ec612715062e40f0bc14a3f Mon Sep 17 00:00:00 2001 From: Einhornimmond Date: Mon, 21 Feb 2022 12:03:19 +0100 Subject: [PATCH] generic filtering, prevent overfetching from db --- backend/src/graphql/resolver/AdminResolver.ts | 40 +++++++++++------- backend/src/typeorm/repository/User.ts | 41 +++---------------- 2 files changed, 32 insertions(+), 49 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 937ed8567..4a2e59644 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx } from 'type-graphql' -import { getCustomRepository, Raw } from '@dbTools/typeorm' +import { getCustomRepository, ObjectLiteral, Raw } from '@dbTools/typeorm' import { UserAdmin, SearchUsersResult } from '../model/UserAdmin' import { PendingCreation } from '../model/PendingCreation' import { CreatePendingCreations } from '../model/CreatePendingCreations' @@ -27,6 +27,13 @@ import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? +/* +parent:any, +args:any, +context:any, +info:any +*/ + @Resolver() export class AdminResolver { @Authorized([RIGHTS.SEARCH_USERS]) @@ -35,21 +42,25 @@ export class AdminResolver { @Args() { searchText, currentPage = 1, pageSize = 25, notActivated = false }: SearchUsersArgs, ): Promise { const userRepository = getCustomRepository(UserRepository) - let users: dbUser[] - let count: number + + const filterCriteria: ObjectLiteral[] = [] if (notActivated) { - [users, count] = await userRepository.findBySearchCriteriaPagedNotActivated( - searchText, - currentPage, - pageSize, - ) - } else { - [users, count] = await userRepository.findBySearchCriteriaPaged( - searchText, - currentPage, - pageSize, - ) + filterCriteria.push({ emailChecked: false }) } + // prevent overfetching data from db, select only needed columns + // prevent reading and transmitting data from db at least 300 Bytes + // one of my example dataset shrink down from 342 Bytes to 42 Bytes, that's ~88% saved db bandwith + const userFields = ['id', 'firstName', 'lastName', 'email', 'emailChecked'] + const [users, count] = await userRepository.findBySearchCriteriaPagedFiltered( + userFields.map((fieldName) => { + return 'user.' + fieldName + }), + searchText, + filterCriteria, + currentPage, + pageSize, + ) + const adminUsers = await Promise.all( users.map(async (user) => { const adminUser = new UserAdmin() @@ -70,6 +81,7 @@ export class AdminResolver { updatedAt: 'DESC', createdAt: 'DESC', }, + select: ['updatedAt', 'createdAt'], }, ) if (emailOptIn) { diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts index 6045c402a..e7ef7f0bc 100644 --- a/backend/src/typeorm/repository/User.ts +++ b/backend/src/typeorm/repository/User.ts @@ -1,4 +1,4 @@ -import { Brackets, EntityRepository, Repository } from '@dbTools/typeorm' +import { Brackets, EntityRepository, ObjectLiteral, Repository } from '@dbTools/typeorm' import { User } from '@entity/User' @EntityRepository(User) @@ -31,44 +31,15 @@ export class UserRepository extends Repository { return usersIndiced } - async findBySearchCriteria(searchCriteria: string): Promise { - return await this.createQueryBuilder('user') - .where( - 'user.firstName like :name or user.lastName like :lastName or user.email like :email', - { - name: `%${searchCriteria}%`, - lastName: `%${searchCriteria}%`, - email: `%${searchCriteria}%`, - }, - ) - .getMany() - } - - async findBySearchCriteriaPaged( - searchCriteria: string, - currentPage: number, - pageSize: number, - ): Promise<[User[], number]> { - return await this.createQueryBuilder('user') - .where( - 'user.firstName like :name or user.lastName like :lastName or user.email like :email', - { - name: `%${searchCriteria}%`, - lastName: `%${searchCriteria}%`, - email: `%${searchCriteria}%`, - }, - ) - .take(pageSize) - .skip((currentPage - 1) * pageSize) - .getManyAndCount() - } - - async findBySearchCriteriaPagedNotActivated( + async findBySearchCriteriaPagedFiltered( + select: string[], searchCriteria: string, + filterCriteria: ObjectLiteral[], currentPage: number, pageSize: number, ): Promise<[User[], number]> { return await this.createQueryBuilder('user') + .select(select) .where( new Brackets((qb) => { qb.where( @@ -81,7 +52,7 @@ export class UserRepository extends Repository { ) }), ) - .andWhere({ emailChecked: false }) + .andWhere(filterCriteria) .take(pageSize) .skip((currentPage - 1) * pageSize) .getManyAndCount()