feat: filter min max cards by authorId, name, favoritedBy

This commit is contained in:
2024-07-30 20:21:17 +02:00
parent da9981533c
commit f3a62cf5b5
5 changed files with 97 additions and 9 deletions

View File

@@ -0,0 +1,29 @@
import { ApiHideProperty } from '@nestjs/swagger'
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators'
import { IsUUIDOrCaller } from '../../../infrastructure/decorators/is-uuid-or-caller'
@ApiSchema({ name: 'MinMaxCardsRequestArgs' })
export class GetMinMaxCardsDto {
/** Search by deck name */
@IsOptionalOrEmptyString()
name?: string
/** Filter by deck authorId
* If ~caller is passed, it will be replaced with the current user's id
*/
@IsOptionalOrEmptyString()
@IsUUIDOrCaller()
authorId?: string
/** Decks favorited by user
* If ~caller is passed, it will be replaced with the current user's id
* */
@IsOptionalOrEmptyString()
@IsUUIDOrCaller()
favoritedBy?: string
@ApiHideProperty()
userId?: string
}

View File

@@ -31,6 +31,7 @@ import {
import { Pagination } from '../../infrastructure/common/pagination/pagination.service'
import { SaveGradeDto } from '../auth/dto'
import { GetMinMaxCardsDto } from '../auth/dto/min-max-cards.dto'
import { JwtAuthGuard } from '../auth/guards'
import { CreateCardDto, GetAllCardsInDeckDto } from '../cards/dto'
import { Card, CardWithGrade, PaginatedCardsWithGrade } from '../cards/entities/cards.entity'
@@ -110,8 +111,13 @@ export class DecksController {
@UseGuards(JwtAuthGuard)
@Version('2')
@Get('min-max-cards')
findMinMaxCards(): Promise<MinMaxCards> {
return this.commandBus.execute(new GetMinMaxCardsUseCaseCommand())
findMinMaxCards(@Query() query: GetMinMaxCardsDto, @Req() req): Promise<MinMaxCards> {
return this.commandBus.execute(
new GetMinMaxCardsUseCaseCommand({
...query,
userId: req.user.id,
})
)
}
@ApiConsumes('multipart/form-data')

View File

@@ -57,9 +57,61 @@ export class DecksRepository {
}
}
async findMinMaxCards(): Promise<{ min: number; max: number }> {
const result = await this.prisma
.$queryRaw`SELECT MAX(card_count) as "maxCardsCount", MIN(card_count) as "minCardsCount" FROM (SELECT deck.id, COUNT(card.id) as card_count FROM flashcards.deck LEFT JOIN "flashcards"."card" ON deck.id = card."deckId" GROUP BY deck.id) AS card_counts;`
async findMinMaxCards({
name,
authorId,
favoritedBy,
userId,
}: {
name?: string
authorId?: string
favoritedBy?: string
userId?: string
}): Promise<{ min: number; max: number }> {
const conditions = []
if (name) conditions.push(`deck.name ILIKE ('%' || ? || '%')`)
if (authorId) conditions.push(`deck."userId" = ?`)
if (userId)
conditions.push(
`(deck."isPrivate" = FALSE OR (deck."isPrivate" = TRUE AND deck."userId" = ?))`
)
if (favoritedBy) conditions.push(`fd."userId" = ?`)
const deckQueryParams = [
userId,
...(name ? [name] : []),
...(authorId ? [authorId] : []),
...(userId ? [userId] : []),
...(favoritedBy ? [favoritedBy] : []),
]
console.log(conditions, deckQueryParams)
const query = `
SELECT MAX(card_count) as "maxCardsCount",
MIN(card_count) as "minCardsCount" FROM (SELECT deck.id, COUNT(card.id) as card_count
FROM flashcards.deck
LEFT JOIN flashcards."favoriteDeck" AS fd ON deck."id" = fd."deckId" AND fd."userId" = $1
LEFT JOIN
"flashcards"."card" ON deck.id = card."deckId"
${
conditions.length
? `WHERE ${conditions
.map(
(condition, index) =>
`${condition.replace(
'?',
`$${index + 2}`
)}`
)
.join(' AND ')}`
: ''
}
GROUP BY deck.id
) AS card_counts;
`
console.log(query)
const result = await this.prisma.$queryRawUnsafe(query, ...deckQueryParams)
return {
max: Number(result[0].maxCardsCount),

View File

@@ -14,7 +14,7 @@ export class GetAllDecksV1Handler implements ICommandHandler<GetAllDecksV1Comman
async execute(command: GetAllDecksV1Command): Promise<PaginatedDecksWithMaxCardsCount> {
const decks = await this.deckRepository.findAllDecks(command.params)
const minMax = await this.deckRepository.findMinMaxCards()
const minMax = await this.deckRepository.findMinMaxCards({})
return { ...decks, maxCardsCount: minMax.max }
}

View File

@@ -1,17 +1,18 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
import { ResendVerificationEmailDto } from '../../auth/dto'
import { MinMaxCards } from '../entities/min-max-cards.entity'
import { DecksRepository } from '../infrastructure/decks.repository'
export class GetMinMaxCardsUseCaseCommand {
constructor() {}
constructor(public readonly args: ResendVerificationEmailDto) {}
}
@CommandHandler(GetMinMaxCardsUseCaseCommand)
export class GetMinMaxCardsUseCaseHandler implements ICommandHandler<GetMinMaxCardsUseCaseCommand> {
constructor(private readonly deckRepository: DecksRepository) {}
async execute(): Promise<MinMaxCards> {
return await this.deckRepository.findMinMaxCards()
async execute(command: GetMinMaxCardsUseCaseCommand): Promise<MinMaxCards> {
return await this.deckRepository.findMinMaxCards(command.args)
}
}